Coverage for apps/ptf/cmds/database_cmds.py: 61%

1157 statements  

« prev     ^ index     » next       coverage.py v7.4.4, created at 2024-05-19 19:20 +0000

1from unidecode import unidecode 

2 

3from django.conf import settings 

4from django.db.models import Q 

5from django.utils import timezone 

6 

7from ptf import exceptions 

8from ptf import model_helpers 

9 

10from ..models import Abstract 

11from ..models import Article 

12from ..models import Author 

13from ..models import Award 

14from ..models import BibItem 

15from ..models import BibItemId 

16from ..models import Collection 

17from ..models import CollectionMembership 

18from ..models import Container 

19from ..models import Contrib 

20from ..models import ContribAddress 

21from ..models import Contribution 

22from ..models import DataStream 

23from ..models import ExtId 

24from ..models import ExtLink 

25from ..models import FrontMatter 

26from ..models import Kwd 

27from ..models import MetaDataPart 

28from ..models import Provider 

29from ..models import PtfSite 

30from ..models import Publisher 

31from ..models import RelatedObject 

32from ..models import RelationName 

33from ..models import Relationship 

34from ..models import ResourceCount 

35from ..models import ResourceId 

36from ..models import SiteMembership 

37from ..models import Stats 

38from ..models import Subj 

39from ..models import SupplementaryMaterial 

40from ..models import TranslatedArticle 

41from ..models import XmlBase 

42from ..models import parse_page_count 

43from .base_cmds import baseCmd 

44from .base_cmds import make_int 

45 

46# TODO: call full_clean before each .save() call to validate a Model object 

47# It requires to update models.py and add some blank=True to fields that can be null/empty 

48# Example: number and vseries for a Container 

49 

50 

51def get_first_letter(name): 

52 letter = "" 

53 if name: 

54 letter = name[0] 

55 letter = unidecode(letter) 

56 if len(letter) > 1: 

57 letter = letter[0] 

58 letter = letter.upper() 

59 if letter == "?": 

60 letter = "Z" 

61 

62 # with 't Hooft, G => first_letter is H 

63 if name.startswith("'t") and len(name) > 3: 

64 letter = name[3].upper() 

65 

66 return letter 

67 

68 

69def add_contributors(contributors, resource=None, bibitem=None): 

70 if resource is None and bibitem is None: 70 ↛ 71line 70 didn't jump to line 71, because the condition on line 70 was never true

71 raise RuntimeError("add_contributors: resource and bibitem are None") 

72 

73 seq = 0 

74 for contrib in contributors: 

75 seq += 1 

76 

77 contrib["seq"] = seq 

78 contribution_fields_list = Contribution.get_fields_list() 

79 contributions_fields = { 

80 key: contrib[key] for key in contribution_fields_list if key in contrib 

81 } 

82 

83 contribution = Contribution(resource=resource, bibitem=bibitem, **contributions_fields) 

84 contribution.save() 

85 

86 for addr in contrib["addresses"]: 

87 contrib_address = ContribAddress(contribution=contribution, address=addr) 

88 contrib_address.save() 

89 

90 if bibitem is None and contribution.role == "author" and not contribution.is_etal(): 

91 ref_name = contribution.mid if contribution.mid else str(contribution) 

92 try: 

93 author = Author.objects.get(name=ref_name) 

94 author.count += 1 

95 except Author.DoesNotExist: 

96 letter = ref_name[0] 

97 letter = unidecode(letter) 

98 if len(letter) > 1: 98 ↛ 99line 98 didn't jump to line 99, because the condition on line 98 was never true

99 letter = letter[0] 

100 letter = letter.upper() 

101 if letter == "?": 101 ↛ 102line 101 didn't jump to line 102, because the condition on line 101 was never true

102 letter = "Z" 

103 

104 # with 't Hooft, G => first_letter is H 

105 if ref_name.startswith("'t") and len(ref_name) > 3: 105 ↛ 106line 105 didn't jump to line 106, because the condition on line 105 was never true

106 letter = ref_name[3].upper() 

107 

108 author = Author(name=ref_name, first_letter=letter, count=1) 

109 author.save() 

110 

111 

112def add_counts(xobj, resource, add_total=False): 

113 seq = 1 

114 for name, value in xobj.counts: 

115 try: 

116 ResourceCount.objects.get(resource=resource, name=name) 

117 raise exceptions.ResourceExists(f"The ResourceCount {name} already exists") 

118 except ResourceCount.DoesNotExist: 

119 resource_count = ResourceCount(resource=resource, name=name, seq=seq, value=value) 

120 

121 resource_count.save() 

122 

123 if name == "page-count" and add_total: 123 ↛ 134line 123 didn't jump to line 134, because the condition on line 123 was never false

124 value = parse_page_count(value) 

125 

126 try: 

127 total = Stats.objects.get(name=name) 

128 total.value += value 

129 except Stats.DoesNotExist: 

130 total = Stats(name=name, value=value) 

131 

132 total.save() 

133 

134 seq += 1 

135 

136 

137def add_biblio(xobj, resource): 

138 for seq, xbibitem in enumerate(xobj.bibitems, start=1): 

139 bibitem = BibItem( 

140 resource=resource, 

141 sequence=seq, 

142 label=xbibitem.label, 

143 citation_xml=xbibitem.citation_xml, 

144 citation_tex=xbibitem.citation_tex, 

145 citation_html=xbibitem.citation_html, 

146 type=xbibitem.type, 

147 user_id=xbibitem.user_id, 

148 article_title_tex=xbibitem.article_title_tex, 

149 chapter_title_tex=xbibitem.chapter_title_tex, 

150 source_tex=xbibitem.source_tex, 

151 publisher_name=xbibitem.publisher_name, 

152 publisher_loc=xbibitem.publisher_loc, 

153 institution=xbibitem.institution, 

154 series=xbibitem.series, 

155 volume=xbibitem.volume, 

156 issue=xbibitem.issue, 

157 month=xbibitem.month, 

158 year=xbibitem.year, 

159 comment=xbibitem.comment, 

160 annotation=xbibitem.annotation, 

161 fpage=xbibitem.fpage, 

162 lpage=xbibitem.lpage, 

163 page_range=xbibitem.page_range, 

164 size=xbibitem.size, 

165 ) 

166 

167 bibitem.save() 

168 

169 add_contributors(contributors=xbibitem.contributors, bibitem=bibitem) 

170 

171 if xbibitem.extids: 

172 the_types = [] 

173 for id_type, id_value in xbibitem.extids: 

174 if id_type and id_type not in the_types: 

175 the_types.append(id_type) 

176 

177 bibitemid = BibItemId( 

178 bibitem=bibitem, 

179 id_type=id_type, 

180 id_value=id_value, 

181 checked=True, 

182 false_positive=False, 

183 ) 

184 

185 bibitemid.save() 

186 

187 

188def add_relations(xobj, resource): 

189 for relation in xobj.relations: 

190 with_doi = "10." in relation.id_value 

191 if with_doi and resource.doi is None: 191 ↛ 192line 191 didn't jump to line 192, because the condition on line 191 was never true

192 raise ValueError( 

193 f"The article {resource.pid} has no DOI but uses the relation {relation.rel_type} with the DOI {relation.id_value}" 

194 ) 

195 

196 # First, we need to find if the relation is stored in the RelationName as a left or right attribute 

197 name = relation.rel_type 

198 relationname = model_helpers.get_relationname_left(left_name=name) 

199 

200 if relationname: 

201 # RelationName with a left attribute (ex "follows"): 

202 # the subject of the relationship is the xml parent (typically the article defined by the xml) 

203 # the object is the id declared by the <related-article> 

204 # Example <article> 

205 # <article_id ...>A</article-id> 

206 # <related-article ... related-article-type="follows">B</related-article> 

207 # => RelationShip with subject = A and object = B 

208 

209 subject_pid = resource.doi if with_doi else resource.pid 

210 subject_resource = resource 

211 object_pid = relation.id_value 

212 # Find the resource to update the related article 

213 # But to work during a regular import, the related article must already be in the database 

214 # Depending on the import order, it might not work 

215 if with_doi: 215 ↛ 216line 215 didn't jump to line 216, because the condition on line 215 was never true

216 object_resource = model_helpers.get_article_by_doi(relation.id_value) 

217 else: 

218 object_resource = model_helpers.get_article(relation.id_value) 

219 else: 

220 relationname = model_helpers.get_relationname_right(right_name=name) 

221 

222 if relationname: 222 ↛ 244line 222 didn't jump to line 244, because the condition on line 222 was never false

223 # RelationName with a right attribute (ex "followed-by"): 

224 # the subject of the relationship is the id declared by the <related-article> 

225 # the object is the xml parent (typically the article defined by the xml) 

226 # Example <article> 

227 # <article_id ...>A</article-id> 

228 # <related-article ... related-article-type="followed-by">B</related-article> 

229 # => RelationShip with subject = B and object = A 

230 

231 subject_pid = relation.id_value 

232 # Find the resource to update the related article 

233 # But to work during a regular import, the related article must already be in the database 

234 # Depending on the import order, it might not work 

235 if with_doi: 235 ↛ 236line 235 didn't jump to line 236, because the condition on line 235 was never true

236 subject_resource = model_helpers.get_article_by_doi(relation.id_value) 

237 else: 

238 subject_resource = model_helpers.get_article(relation.id_value) 

239 

240 object_pid = resource.doi if with_doi else resource.pid 

241 object_resource = resource 

242 

243 # Elsevier creates basic relation. Create the relationname on the fly if necessary 

244 if not relationname and name == "refers to": 244 ↛ 245line 244 didn't jump to line 245, because the condition on line 244 was never true

245 relationname = RelationName( 

246 left="refers to", 

247 right="is referenced by", 

248 gauche="fait référence à", 

249 droite="est référencé par", 

250 ) 

251 relationname.save() 

252 

253 subject_pid = resource.doi if with_doi else resource.pid 

254 subject_resource = resource 

255 object_pid = relation.id_value 

256 # Find the resource to update the related article 

257 # But to work during a regular import, the related article must already be in the database 

258 # Depending on the import order, it might not work 

259 if with_doi: 

260 object_resource = model_helpers.get_article_by_doi(relation.id_value) 

261 else: 

262 object_resource = model_helpers.get_article(relation.id_value) 

263 

264 if relationname: 264 ↛ 189line 264 didn't jump to line 189, because the condition on line 264 was never false

265 params = {"subject_pid": subject_pid, "object_pid": object_pid, "solr_commit": False} 

266 cmd = addRelationshipDatabaseCmd(params) 

267 if subject_resource is not None: 

268 cmd.set_subject_resource(subject_resource) 

269 if object_resource is not None: 

270 cmd.set_object_resource(object_resource) 

271 cmd.set_relationname(relationname) 

272 

273 try: 

274 relationship = Relationship.objects.get( 

275 subject_pid=subject_pid, object_pid=object_pid, rel_info=relationname 

276 ) 

277 # la première fois que l'on crée la relation une des 2 ressources ID est a priori None 

278 # la deuxième fois (la relation symétrique) il faut compléter la 

279 # RelationShip 

280 if relationship.resource is not None and relationship.related is not None: 280 ↛ 281line 280 didn't jump to line 281, because the condition on line 280 was never true

281 raise exceptions.ResourceExists( 

282 f"The Relationship {subject_pid} {relationname.left} {object_pid} already exists" 

283 ) 

284 if subject_resource is not None: 284 ↛ 286line 284 didn't jump to line 286, because the condition on line 284 was never false

285 relationship.resource = subject_resource 

286 if object_resource is not None: 286 ↛ 288line 286 didn't jump to line 288, because the condition on line 286 was never false

287 relationship.related = object_resource 

288 relationship.save() 

289 

290 except Relationship.DoesNotExist: 

291 relationship = Relationship( 

292 subject_pid=subject_pid, 

293 object_pid=object_pid, 

294 resource=subject_resource, 

295 related=object_resource, 

296 rel_info=relationname, 

297 ) 

298 relationship.save() 

299 

300 

301def add_frontmatter(xobj, resource): 

302 if hasattr(xobj, "frontmatter_xml") and xobj.frontmatter_xml is not None: 

303 frontmatter = FrontMatter( 

304 resource=resource, 

305 value_xml=xobj.frontmatter_xml, 

306 value_html=xobj.frontmatter_toc_html, 

307 foreword_html=xobj.frontmatter_foreword_html, 

308 ) 

309 frontmatter.save() 

310 

311 

312def add_publisher(xobj): 

313 if hasattr(xobj, "publisher") and xobj.publisher: 

314 publisher = model_helpers.get_publisher(xobj.publisher.name) 

315 

316 if publisher is None: 

317 cmd = addPublisherDatabaseCmd({"xobj": xobj.publisher}) 

318 publisher = cmd.do() 

319 

320 return publisher 

321 

322 

323class addSiteDatabaseCmd(baseCmd): 

324 """ 

325 addSiteDatabaseCmd: adds/remove a PtfSite 

326 

327 Exception raised: 

328 - ValueError if the init params are empty 

329 - exceptions.ResourceExists during do if the site already exists 

330 - exceptions.ResourceDoesNotExist during undo if the site does not exist 

331 - RuntimeError during undo if resources are still deployed 

332 """ 

333 

334 def __init__(self, params=None): 

335 self.site_name = None 

336 self.site_domain = None 

337 self.site_id = None 

338 

339 super().__init__(params) 

340 

341 if not self.site_name: 341 ↛ 342line 341 didn't jump to line 342, because the condition on line 341 was never true

342 self.site_name = settings.SITE_NAME 

343 

344 if not self.site_domain: 344 ↛ 345line 344 didn't jump to line 345, because the condition on line 344 was never true

345 self.site_domain = settings.SITE_DOMAIN 

346 

347 self.required_params.extend(["site_name", "site_domain", "site_id"]) 

348 

349 # Returns a PtfSite 

350 def internal_do(self): 

351 super().internal_do() 

352 

353 try: 

354 PtfSite.objects.get(name=self.site_name) 

355 raise exceptions.ResourceExists( 

356 "The site " + self.site_name + " " + self.site_domain + " already exists" 

357 ) 

358 except PtfSite.DoesNotExist: 

359 site = PtfSite( 

360 domain=self.site_domain, name=self.site_name, acro=self.site_name, id=self.site_id 

361 ) 

362 site.save() 

363 

364 return site 

365 

366 def pre_undo(self): 

367 super().pre_undo() 

368 

369 if SiteMembership.objects.filter(site__name=self.site_name).exists(): 

370 raise RuntimeError( 

371 "Impossible de supprimer le site car il y a encore des ressources publiees" 

372 ) 

373 

374 def internal_undo(self): 

375 super().internal_undo() 

376 

377 try: 

378 # may throw PtfSite.DoesNotExist 

379 site = PtfSite.objects.get(name=self.site_name) 

380 id = site.id 

381 site.delete() 

382 except PtfSite.DoesNotExist: 

383 raise exceptions.ResourceDoesNotExist("The site " + self.site_name + " does not exist") 

384 

385 return id 

386 

387 

388class addProviderDatabaseCmd(baseCmd): 

389 """ 

390 addProviderDatabaseCmd: adds/remove a Provider 

391 

392 Exception raised: 

393 - ValueError if the init params are empty 

394 - exceptions.ResourceExists during do if the provider already exists 

395 - exceptions.ResourceDoesNotExist during undo if the provider does not exist 

396 """ 

397 

398 def __init__(self, params=None): 

399 self.name = None 

400 self.pid_type = None 

401 self.sid_type = None 

402 

403 super().__init__(params) 

404 

405 self.required_params.extend(["name", "pid_type"]) 

406 

407 def internal_do(self): 

408 super().internal_do() 

409 

410 try: 

411 Provider.objects.get(name=self.name, pid_type=self.pid_type, sid_type=self.sid_type) 

412 raise exceptions.ResourceExists("The provider " + self.name + " already exists") 

413 except Provider.DoesNotExist: 

414 p = Provider(name=self.name, pid_type=self.pid_type, sid_type=self.sid_type) 

415 p.save() 

416 

417 return p 

418 

419 def internal_undo(self): 

420 super().internal_undo() 

421 

422 try: 

423 p = Provider.objects.get( 

424 name=self.name, pid_type=self.pid_type, sid_type=self.sid_type 

425 ) 

426 id = p.id 

427 p.delete() 

428 except Provider.DoesNotExist: 

429 raise exceptions.ResourceDoesNotExist("The provider " + self.name + " does not exist") 

430 

431 return id 

432 

433 

434class addXmlBaseDatabaseCmd(baseCmd): 

435 """ 

436 addXmlBaseDatabaseCmd: adds/remove an XmlBase 

437 

438 XmlBase is the root URL of an ExtLink (ex: http://archive.numdam.org/article) 

439 

440 Exception raised: 

441 - ValueError if the init params are empty 

442 - exceptions.ResourceExists during do if the XmlBase already exists 

443 - exceptions.ResourceDoesNotExist during undo if the XmlBase does not exist 

444 - RuntimeError during undo if related extlinks or objects still exist 

445 """ 

446 

447 def __init__(self, params=None): 

448 self.base = None # Ex: http://archive.numdam.org/article 

449 

450 super().__init__(params) 

451 

452 self.required_params.extend(["base"]) 

453 

454 def internal_do(self): 

455 super().internal_do() 

456 

457 try: 

458 XmlBase.objects.get(base=self.base) 

459 raise exceptions.ResourceExists("The xmlbase " + self.base + " already exists") 

460 except XmlBase.DoesNotExist: 

461 xmlbase = XmlBase(base=self.base) 

462 xmlbase.save() 

463 

464 return xmlbase 

465 

466 def internal_undo(self): 

467 super().internal_undo() 

468 

469 try: 

470 xmlbase = XmlBase.objects.get(base=self.base) 

471 id = xmlbase.id 

472 except XmlBase.DoesNotExist: 

473 raise exceptions.ResourceDoesNotExist("The xmlbase " + self.base + " does not exist") 

474 

475 try: 

476 extlink = ExtLink.objects.get(base=xmlbase) 

477 

478 if extlink: 

479 raise RuntimeError( 

480 "Impossible de supprimer cette ressource car elle est encore utilisee par des ExtLinks" 

481 ) 

482 

483 except ExtLink.DoesNotExist: 

484 pass 

485 

486 try: 

487 related_object = RelatedObject.objects.get(base=xmlbase) 

488 

489 if related_object: 

490 raise RuntimeError( 

491 "Impossible de supprimer cette ressource car elle est encore utilisee par des RelatedObjects" 

492 ) 

493 

494 except RelatedObject.DoesNotExist: 

495 pass 

496 

497 xmlbase.delete() 

498 return id 

499 

500 

501class addExtLinkDatabaseCmd(baseCmd): 

502 """ 

503 addExtLinkDatabaseCmd: adds/remove an ExtLink 

504 An extlink is a link to an external object (image...) 

505 

506 Exception raised: 

507 - ValueError if the init params are empty 

508 - exceptions.ResourceExists during do if the ExtLink already exists 

509 - exceptions.ResourceDoesNotExist during undo if the ExtLink does not exist 

510 - RuntimeError during undo if resources are still deployed 

511 """ 

512 

513 def __init__(self, params=None): 

514 self.rel = None # 'website' or 'small_icon' 

515 self.mimetype = None 

516 self.location = None 

517 self.metadata = None 

518 self.seq = None 

519 self.resource = None 

520 self.base = None 

521 

522 super().__init__(params) 

523 

524 self.required_params.extend(["rel", "location", "seq", "resource"]) 

525 

526 def set_resource(self, resource): 

527 self.resource = resource 

528 

529 def set_base(self, base): 

530 self.base = base 

531 

532 def internal_do(self): 

533 super().internal_do() 

534 

535 try: 

536 ExtLink.objects.get( 

537 resource=self.resource, 

538 base=self.base, 

539 rel=self.rel, 

540 mimetype=self.mimetype, 

541 location=self.location, 

542 metadata=self.metadata, 

543 seq=self.seq, 

544 ) 

545 raise exceptions.ResourceExists( 

546 "The ExtLink " + self.base + " " + self.rel + " already exists" 

547 ) 

548 except ExtLink.DoesNotExist: 

549 extlink = ExtLink( 

550 resource=self.resource, 

551 base=self.base, 

552 rel=self.rel, 

553 mimetype=self.mimetype, 

554 location=self.location, 

555 metadata=self.metadata, 

556 seq=self.seq, 

557 ) 

558 extlink.save() 

559 

560 return extlink 

561 

562 def internal_undo(self): 

563 super().internal_undo() 

564 

565 try: 

566 extlink = ExtLink.objects.get(resource=self.resource, base=self.base, rel=self.rel) 

567 id = extlink.id 

568 extlink.delete() 

569 except ExtLink.DoesNotExist: 

570 raise exceptions.ResourceDoesNotExist("The extlink " + self.rel + " does not exist") 

571 

572 return id 

573 

574 

575class addExtIdDatabaseCmd(baseCmd): 

576 """ 

577 addExtIdDatabaseCmd: adds/remove an ExtId 

578 

579 Exception raised: 

580 - ValueError if the init params are empty 

581 - exceptions.ResourceExists during do if the ExtId already exists 

582 - exceptions.ResourceDoesNotExist during undo if the ExtId does not exist 

583 - RuntimeError during undo if resources are still deployed 

584 """ 

585 

586 def __init__(self, params=None): 

587 self.resource = None 

588 self.id_type = None 

589 self.id_value = None 

590 self.checked = True 

591 self.false_positive = False 

592 

593 super().__init__(params) 

594 

595 self.required_params.extend(["id_type", "id_value", "resource"]) 

596 

597 def set_resource(self, resource): 

598 self.resource = resource 

599 

600 def internal_do(self): 

601 super().internal_do() 

602 

603 try: 

604 # May raise Resource.DoesNotExist 

605 extid = ExtId.objects.get( 

606 resource=self.resource, id_type=self.id_type, id_value=self.id_value 

607 ) 

608 except ExtId.DoesNotExist: 

609 extid = ExtId( 

610 resource=self.resource, 

611 id_type=self.id_type, 

612 id_value=self.id_value, 

613 checked=self.checked, 

614 false_positive=self.false_positive, 

615 ) 

616 extid.save() 

617 

618 return extid 

619 

620 def internal_undo(self): 

621 super().internal_undo() 

622 

623 try: 

624 extid = ExtId.objects.get( 

625 resource=self.resource, id_type=self.id_type, id_value=self.id_value 

626 ) 

627 id = extid.id 

628 extid.delete() 

629 except ExtId.DoesNotExist: 

630 raise exceptions.ResourceDoesNotExist("The extid " + self.id_value + " does not exist") 

631 

632 return id 

633 

634 

635class addRelatedObjectDatabaseCmd(baseCmd): 

636 """ 

637 addRelatedObjectDatabaseCmd: adds/remove a RelatedObject 

638 

639 a related object is a pdf/djvu... attached to the object (article...) 

640 It can be for example a listing of a program attached to an article. 

641 

642 Exception raised: 

643 - ValueError if the init params are empty 

644 - exceptions.ResourceExists during do if the RelatedObject already exists 

645 - exceptions.ResourceDoesNotExist during undo if the RelatedObject does not exist 

646 - RuntimeError during undo if resources are still deployed 

647 """ 

648 

649 def __init__(self, params=None): 

650 self.rel = None 

651 self.mimetype = None 

652 self.location = None 

653 self.metadata = None 

654 self.seq = None 

655 self.resource = None 

656 self.base = None 

657 

658 super().__init__(params) 

659 

660 self.required_params.extend(["rel", "location", "seq", "resource"]) 

661 

662 def set_resource(self, resource): 

663 self.resource = resource 

664 

665 def set_base(self, base): 

666 self.base = base 

667 

668 def internal_do(self): 

669 super().internal_do() 

670 

671 try: 

672 RelatedObject.objects.get( 

673 resource=self.resource, 

674 base=self.base, 

675 rel=self.rel, 

676 mimetype=self.mimetype, 

677 location=self.location, 

678 metadata=self.metadata, 

679 seq=self.seq, 

680 ) 

681 raise exceptions.ResourceExists( 

682 "The RelatedObject " 

683 + self.base 

684 + " " 

685 + self.rel 

686 + " " 

687 + self.location 

688 + " already exists" 

689 ) 

690 except RelatedObject.DoesNotExist: 

691 related_object = RelatedObject( 

692 resource=self.resource, 

693 base=self.base, 

694 rel=self.rel, 

695 mimetype=self.mimetype, 

696 location=self.location, 

697 metadata=self.metadata, 

698 seq=self.seq, 

699 ) 

700 

701 related_object.save() 

702 

703 return related_object 

704 

705 def internal_undo(self): 

706 super().internal_undo() 

707 

708 try: 

709 related_object = RelatedObject.objects.get( 

710 resource=self.resource, base=self.base, rel=self.rel, location=self.location 

711 ) 

712 id = related_object.id 

713 related_object.delete() 

714 except RelatedObject.DoesNotExist: 

715 raise exceptions.ResourceDoesNotExist( 

716 "The relatedobject " + self.location + " does not exist" 

717 ) 

718 

719 return id 

720 

721 

722class addSupplementaryMaterialDatabaseCmd(addRelatedObjectDatabaseCmd): 

723 """ 

724 addSupplementaryMaterialDatabaseCmd: adds/remove a Supplementary Materiel 

725 

726 a supplementary material is a pdf/djvu... attached to the object (article...) 

727 It can be for example a listing of a program attached to an article. 

728 

729 Exception raised: 

730 - ValueError if the init params are empty 

731 - exceptions.ResourceExists during do if the RelatedObject already exists 

732 - exceptions.ResourceDoesNotExist during undo if the RelatedObject does not exist 

733 - RuntimeError during undo if resources are still deployed 

734 """ 

735 

736 def __init__(self, params=None): 

737 super().__init__(params) 

738 

739 def internal_do(self): 

740 supplementary_material, _created = SupplementaryMaterial.objects.update_or_create( 

741 caption=self.caption, 

742 resource=self.resource, 

743 base=self.base, 

744 rel=self.rel, 

745 mimetype=self.mimetype, 

746 location=self.location, 

747 metadata=self.metadata, 

748 seq=self.seq, 

749 ) 

750 return supplementary_material 

751 

752 def internal_undo(self): 

753 try: 

754 supplementary_material = SupplementaryMaterial.objects.get( 

755 resource=self.resource, base=self.base, rel=self.rel, location=self.location 

756 ) 

757 pk = supplementary_material.pk 

758 supplementary_material.delete() 

759 except SupplementaryMaterial.DoesNotExist: 

760 raise exceptions.ResourceDoesNotExist( 

761 "The SupplementaryMaterial " + self.location + " does not exist" 

762 ) 

763 return pk 

764 

765 

766class addDataStreamDatabaseCmd(baseCmd): 

767 """ 

768 addDataStreamDatabaseCmd: adds/remove a DataStream 

769 

770 a datastream is the pdf/djvu... of the object (container, article) 

771 

772 Exception raised: 

773 - ValueError if the init params are empty 

774 - exceptions.ResourceExists during do if the DataStream already exists 

775 - exceptions.ResourceDoesNotExist during undo if the DataStream does not exist 

776 - RuntimeError during undo if resources are still deployed 

777 """ 

778 

779 def __init__(self, params=None): 

780 self.rel = None 

781 self.mimetype = None 

782 self.location = None 

783 self.text = None 

784 self.seq = None 

785 self.resource = None 

786 self.base = None 

787 

788 super().__init__(params) 

789 

790 self.required_params.extend(["rel", "location", "seq", "resource"]) 

791 

792 def set_resource(self, resource): 

793 self.resource = resource 

794 

795 def set_base(self, base): 

796 self.base = base 

797 

798 def internal_do(self): 

799 super().internal_do() 

800 

801 try: 

802 DataStream.objects.get( 

803 resource=self.resource, 

804 base=self.base, 

805 rel=self.rel, 

806 mimetype=self.mimetype, 

807 location=self.location, 

808 text=self.text, 

809 seq=self.seq, 

810 ) 

811 raise exceptions.ResourceExists( 

812 "The DataStream " 

813 + self.base 

814 + " " 

815 + self.rel 

816 + " " 

817 + self.location 

818 + " already exists" 

819 ) 

820 except DataStream.DoesNotExist: 

821 datastream = DataStream( 

822 resource=self.resource, 

823 base=self.base, 

824 rel=self.rel, 

825 mimetype=self.mimetype, 

826 location=self.location, 

827 text=self.text, 

828 seq=self.seq, 

829 ) 

830 

831 datastream.save() 

832 

833 return datastream 

834 

835 def internal_undo(self): 

836 super().internal_undo() 

837 

838 try: 

839 datastream = DataStream.objects.get( 

840 resource=self.resource, base=self.base, rel=self.rel, location=self.location 

841 ) 

842 id = datastream.id 

843 datastream.delete() 

844 except DataStream.DoesNotExist: 

845 raise exceptions.ResourceDoesNotExist( 

846 "The datastream " + self.location + " does not exist" 

847 ) 

848 

849 return id 

850 

851 

852class addResourceCountDatabaseCmd(baseCmd): 

853 """ 

854 addResourceCountDatabaseCmd: adds/remove a ResourceCount 

855 

856 A ResourceCount is a generic count element. 

857 Exemple: page count, table count, image count... 

858 

859 Exception raised: 

860 - ValueError if the init params are empty 

861 - exceptions.ResourceExists during do if the ResourceCount already exists 

862 - exceptions.ResourceDoesNotExist during undo if the ResourceCount does not exist 

863 - RuntimeError during undo if resources are still deployed 

864 """ 

865 

866 def __init__(self, params=None): 

867 self.seq = None 

868 self.name = None 

869 self.value = None 

870 self.resource = None 

871 self.add_total = False 

872 

873 super().__init__(params) 

874 

875 self.required_params.extend(["seq", "name", "value", "resource"]) 

876 

877 def set_resource(self, resource): 

878 self.resource = resource 

879 

880 def internal_do(self): 

881 super().internal_do() 

882 

883 try: 

884 ResourceCount.objects.get(resource=self.resource, name=self.name) 

885 raise exceptions.ResourceExists("The ResourceCount " + self.name + " already exists") 

886 except ResourceCount.DoesNotExist: 

887 resource_count = ResourceCount( 

888 resource=self.resource, name=self.name, seq=self.seq, value=self.value 

889 ) 

890 

891 resource_count.save() 

892 

893 if self.name == "page-count" and self.add_total: 

894 value = parse_page_count(self.value) 

895 

896 try: 

897 total = Stats.objects.get(name=self.name) 

898 total.value += value 

899 except Stats.DoesNotExist: 

900 total = Stats(name=self.name, value=value) 

901 

902 total.save() 

903 

904 return resource_count 

905 

906 def internal_undo(self): 

907 super().internal_undo() 

908 

909 try: 

910 resource_count = ResourceCount.objects.get(resource=self.resource, name=self.name) 

911 id = resource_count.id 

912 resource_count.delete() 

913 except ResourceCount.DoesNotExist: 

914 raise exceptions.ResourceDoesNotExist("The count " + self.name + " does not exist") 

915 

916 return id 

917 

918 

919class addMetaDataPartDatabaseCmd(baseCmd): 

920 """ 

921 addMetaDataPartDatabaseCmd: adds/remove a MetaDataPart 

922 

923 A MetaDataPart is a generic count element. 

924 Exemple: page count, table count, image count... 

925 

926 Exception raised: 

927 - ValueError if the init params are empty 

928 - exceptions.ResourceExists during do if the MetaDataPart already exists 

929 - exceptions.ResourceDoesNotExist during undo if the MetaDataPart does not exist 

930 - RuntimeError during undo if resources are still deployed 

931 """ 

932 

933 def __init__(self, params=None): 

934 self.seq = None 

935 self.name = None 

936 self.data = None 

937 self.resource = None 

938 

939 super().__init__(params) 

940 

941 self.required_params.extend(["seq", "name", "data", "resource"]) 

942 

943 def set_resource(self, resource): 

944 self.resource = resource 

945 

946 def internal_do(self): 

947 super().internal_do() 

948 

949 try: 

950 MetaDataPart.objects.get(resource=self.resource, name=self.name) 

951 raise exceptions.ResourceExists("The MetaDataPart " + self.name + " already exists") 

952 except MetaDataPart.DoesNotExist: 

953 metadatapart = MetaDataPart( 

954 resource=self.resource, name=self.name, seq=self.seq, data=self.data 

955 ) 

956 

957 metadatapart.save() 

958 

959 return metadatapart 

960 

961 def internal_undo(self): 

962 super().internal_undo() 

963 

964 try: 

965 metadatapart = MetaDataPart.objects.get(resource=self.resource, name=self.name) 

966 id = metadatapart.id 

967 metadatapart.delete() 

968 except MetaDataPart.DoesNotExist: 

969 raise exceptions.ResourceDoesNotExist( 

970 "The metadatapart " + self.name + " does not exist" 

971 ) 

972 

973 return id 

974 

975 

976class addBibItemDatabaseCmd(baseCmd): 

977 """ 

978 addBibItemDatabaseCmd: adds/remove a BibItem 

979 

980 No verification is done to check if a BibItem already exists 

981 Rationale: BibItems are only added in a loop within an article. 

982 The check is actually the existence of the article. 

983 

984 Exception raised: 

985 - ValueError if the init params are empty 

986 - exceptions.ResourceDoesNotExist during undo if the BibItem does not exist 

987 - RuntimeError during undo if resources are still deployed 

988 """ 

989 

990 def __init__(self, params=None): 

991 self.sequence = None 

992 self.label = "" 

993 self.citation_xml = None 

994 self.citation_tex = None 

995 self.citation_html = None 

996 self.resource = None 

997 

998 self.type = "" 

999 self.user_id = "" 

1000 self.title_tex = "" 

1001 self.publisher_name = "" 

1002 self.publisher_loc = "" 

1003 self.institution = "" 

1004 self.series = "" 

1005 self.volume = "" 

1006 self.issue = "" 

1007 self.month = "" 

1008 self.year = "" 

1009 self.comment = "" 

1010 self.annotation = "" 

1011 self.fpage = "" 

1012 self.lpage = "" 

1013 self.page_range = "" 

1014 self.size = "" 

1015 self.source = "" 

1016 self.article_title_tex = "" 

1017 self.chapter_title_tex = "" 

1018 

1019 self.contributors = None 

1020 

1021 super().__init__(params) 

1022 

1023 self.required_params.extend(["sequence", "citation_xml", "resource"]) 

1024 

1025 def set_resource(self, resource): 

1026 self.resource = resource 

1027 

1028 def internal_do(self): 

1029 super().internal_do() 

1030 

1031 bibitem = BibItem( 

1032 resource=self.resource, 

1033 sequence=self.sequence, 

1034 label=self.label, 

1035 citation_xml=self.citation_xml, 

1036 citation_tex=self.citation_tex, 

1037 citation_html=self.citation_html, 

1038 type=self.type, 

1039 user_id=self.user_id, 

1040 article_title_tex=self.article_title_tex, 

1041 chapter_title_tex=self.chapter_title_tex, 

1042 source_tex=self.source_tex, 

1043 publisher_name=self.publisher_name, 

1044 publisher_loc=self.publisher_loc, 

1045 institution=self.institution, 

1046 series=self.series, 

1047 volume=self.volume, 

1048 issue=self.issue, 

1049 month=self.month, 

1050 year=self.year, 

1051 comment=self.comment, 

1052 annotation=self.annotation, 

1053 fpage=self.fpage, 

1054 lpage=self.lpage, 

1055 page_range=self.page_range, 

1056 size=self.size, 

1057 ) 

1058 

1059 bibitem.save() 

1060 

1061 return bibitem 

1062 

1063 def post_do(self, bibitem): 

1064 super().post_do(bibitem) 

1065 

1066 add_contributors(contributors=self.contributors, bibitem=bibitem) 

1067 

1068 def internal_undo(self): 

1069 super().internal_undo() 

1070 

1071 try: 

1072 bibitem = BibItem.objects.get(resource=self.resource, sequence=self.sequence) 

1073 id = bibitem.id 

1074 bibitem.delete() 

1075 except BibItem.DoesNotExist: 

1076 raise exceptions.ResourceDoesNotExist( 

1077 "The bibitem " + self.sequence + " does not exist" 

1078 ) 

1079 

1080 return id 

1081 

1082 

1083class addBibItemIdDatabaseCmd(baseCmd): 

1084 """ 

1085 addBibItemIdDatabaseCmd: adds/remove a BibItemId 

1086 

1087 No verification is done to check if a BibItemId already exists 

1088 Rationale: BibItems are only added in a loop within an article. 

1089 The check is actually the existence of the article. 

1090 

1091 Exception raised: 

1092 - ValueError if the init params are empty 

1093 - exceptions.ResourceDoesNotExist during undo if the BibItemId does not exist 

1094 - RuntimeError during undo if resources are still deployed 

1095 """ 

1096 

1097 def __init__(self, params=None): 

1098 self.bibitem = None 

1099 self.id_type = None 

1100 self.id_value = None 

1101 self.checked = True 

1102 self.false_positive = False 

1103 

1104 super().__init__(params) 

1105 

1106 self.required_params.extend(["bibitem", "id_type", "id_value"]) 

1107 

1108 def set_bibitem(self, bibitem): 

1109 self.bibitem = bibitem 

1110 

1111 def internal_do(self): 

1112 super().internal_do() 

1113 

1114 bibitemid = BibItemId( 

1115 bibitem=self.bibitem, 

1116 id_type=self.id_type, 

1117 id_value=self.id_value, 

1118 checked=self.checked, 

1119 false_positive=self.false_positive, 

1120 ) 

1121 

1122 bibitemid.save() 

1123 

1124 return bibitemid 

1125 

1126 def internal_undo(self): 

1127 super().internal_undo() 

1128 

1129 try: 

1130 bibitemid = BibItemId.objects.get( 

1131 bibitem=self.bibitem, id_type=self.id_type, id_value=self.id_value 

1132 ) 

1133 id = bibitemid.id 

1134 bibitemid.delete() 

1135 except BibItemId.DoesNotExist: 

1136 raise exceptions.ResourceDoesNotExist( 

1137 "The bibitemid " + self.value + " does not exist" 

1138 ) 

1139 

1140 return id 

1141 

1142 

1143class addFrontMatterDatabaseCmd(baseCmd): 

1144 """ 

1145 addFrontMatterDatabaseCmd: adds/remove a FrontMatter 

1146 

1147 No verification is done to check if a FrontMatter already exists 

1148 Rationale: FrontMatters are only added in a loop within an article. 

1149 The check is actually the existence of the article. 

1150 

1151 Exception raised: 

1152 - ValueError if the init params are empty 

1153 - exceptions.ResourceDoesNotExist during undo if the FrontMatter does not exist 

1154 - RuntimeError during undo if resources are still deployed 

1155 """ 

1156 

1157 def __init__(self, params=None): 

1158 self.value_xml = "" 

1159 self.value_html = "" 

1160 self.foreword_html = "" 

1161 self.resource = None 

1162 

1163 super().__init__(params) 

1164 

1165 self.required_params.extend(["value_xml", "resource"]) 

1166 

1167 def set_resource(self, resource): 

1168 self.resource = resource 

1169 

1170 def internal_do(self): 

1171 super().internal_do() 

1172 

1173 frontmatter = FrontMatter( 

1174 resource=self.resource, 

1175 value_xml=self.value_xml, 

1176 value_html=self.value_html, 

1177 foreword_html=self.foreword_html, 

1178 ) 

1179 

1180 frontmatter.save() 

1181 

1182 return frontmatter 

1183 

1184 def internal_undo(self): 

1185 super().internal_undo() 

1186 

1187 try: 

1188 frontmatter = FrontMatter.objects.get(resource=self.resource) 

1189 id = frontmatter.id 

1190 frontmatter.delete() 

1191 except FrontMatter.DoesNotExist: 

1192 raise exceptions.ResourceDoesNotExist( 

1193 "The front-matter " + self.text + " does not exist" 

1194 ) 

1195 

1196 return id 

1197 

1198 

1199class addRelationshipDatabaseCmd(baseCmd): 

1200 """ 

1201 addRelationshipDatabaseCmd: adds/remove a Relationship 

1202 

1203 Relationship relates 2 resources (ex: articles) with a relation. ex "follows", "followed-by" 

1204 

1205 RelationName are created with a fixture (see app/ptf/apps/ptf/fixtures/initial_data.json 

1206 Example { "left" : "follows", "right" : "followed-by" } 

1207 A related-article of an article has 1 relation name (ex "follows" or "followed-by") 

1208 You need to know if the relation was stored in the left or right attribute of a RelationName, 

1209 so that you can create/search the Relationship with the correct object/subject. 

1210 Ex: with A "follows" B, A is the subject and B the object because "follows" is a RelationName.left attribute 

1211 with A "followed-by" B, A is the object the B the subject because 

1212 "followed-by" is a RelationName.right attribute 

1213 A Relationship relates 2 resources with a RelationName 

1214 

1215 

1216 Exception raised: 

1217 - ValueError if the init params are empty 

1218 - exceptions.ResourceExists during do if the Relationship already exists 

1219 - exceptions.ResourceDoesNotExist during undo if the Relationship does not exist 

1220 - RuntimeError during undo if resources are still deployed 

1221 """ 

1222 

1223 def __init__(self, params=None): 

1224 self.subject_resource = None 

1225 self.subject_pid = None 

1226 self.object_resource = None 

1227 self.object_pid = None 

1228 self.relationname = None 

1229 

1230 super().__init__(params) 

1231 

1232 self.required_params.extend(["subject_pid", "object_pid", "relationname"]) 

1233 

1234 def set_subject_resource(self, resource): 

1235 self.subject_resource = resource 

1236 # self.subject_pid = resource.pid 

1237 

1238 def set_object_resource(self, resource): 

1239 self.object_resource = resource 

1240 # self.object_pid = resource.pid 

1241 

1242 def set_relationname(self, relationname): 

1243 self.relationname = relationname 

1244 

1245 def internal_do(self): 

1246 super().internal_do() 

1247 

1248 try: 

1249 relationship = Relationship.objects.get( 

1250 subject_pid=self.subject_pid, 

1251 object_pid=self.object_pid, 

1252 rel_info=self.relationname, 

1253 ) 

1254 # la première fois que l'on crée la relation une des 2 ressourcesID est a priori None 

1255 # la deuxième fois (la relation symétrique) il faut compléter la 

1256 # RelationShip 

1257 if relationship.resource is not None and relationship.related is not None: 

1258 raise exceptions.ResourceExists( 

1259 "The Relationship {} {} {} already exists".format( 

1260 self.subject_pid, self.relationname.left, self.object_pid 

1261 ) 

1262 ) 

1263 if self.subject_resource is not None: 

1264 relationship.resource = self.subject_resource 

1265 if self.object_resource is not None: 

1266 relationship.related = self.object_resource 

1267 relationship.save() 

1268 

1269 except Relationship.DoesNotExist: 

1270 relationship = Relationship( 

1271 subject_pid=self.subject_pid, 

1272 object_pid=self.object_pid, 

1273 resource=self.subject_resource, 

1274 related=self.object_resource, 

1275 rel_info=self.relationname, 

1276 ) 

1277 

1278 relationship.save() 

1279 

1280 return relationship 

1281 

1282 def internal_undo(self): 

1283 super().internal_undo() 

1284 

1285 try: 

1286 relationship = Relationship.objects.get( 

1287 subject_pid=self.subject_pid, 

1288 object_pid=self.object_pid, 

1289 rel_info=self.relationname, 

1290 ) 

1291 id = relationship.id 

1292 

1293 # Create relationship is typically a 2 steps process 

1294 # (1: create the relationship with only 1 resource. 2: update the relationship with the 2nd resource) 

1295 # Undo is also a 2 steps process 

1296 # (1: unset a resource. 2: delete the relationship) 

1297 if relationship.resource is not None and relationship.related is not None: 

1298 # Both left and right resources are set: unset the resource 

1299 # that was given as param 

1300 if self.subject_resource is None: 

1301 relationship.related = None 

1302 else: 

1303 relationship.resource = None 

1304 relationship.save() 

1305 else: 

1306 relationship.delete() 

1307 except Relationship.DoesNotExist: 

1308 raise exceptions.ResourceDoesNotExist( 

1309 "The relationship " + self.relationname + " does not exist" 

1310 ) 

1311 

1312 return id 

1313 

1314 

1315class addResourceDatabaseCmd(baseCmd): 

1316 """ 

1317 addResourceDatabaseCmd: base class for all resources 

1318 

1319 Exception raised: 

1320 - exceptions.ResourceDoesNotExist during undo if the Resource does not exist 

1321 """ 

1322 

1323 def __init__(self, params=None): 

1324 self.xobj = None # model_data object 

1325 

1326 self.provider = None 

1327 self._prod_deployed_date = None 

1328 

1329 super().__init__(params) 

1330 

1331 self.required_params.extend(["xobj", "provider"]) 

1332 

1333 # May raise ValueError 

1334 def check_params(self): 

1335 super().check_params() 

1336 

1337 if hasattr(self.xobj, "pid") and not self.xobj.pid and not self.xobj.sid: 1337 ↛ 1338line 1337 didn't jump to line 1338, because the condition on line 1337 was never true

1338 raise ValueError("pid et sid sont vides") 

1339 

1340 def set_provider(self, provider): 

1341 self.provider = provider 

1342 

1343 # May raise Provider.DoesNotExist 

1344 def set_provider_by_name_or_id(self, provider_name="", pid_type="", sid_type=None): 

1345 self.provider = Provider.objects.get( 

1346 name=provider_name, pid_type=pid_type, sid_type=sid_type 

1347 ) 

1348 

1349 def post_do(self, resource): 

1350 super().post_do(resource) 

1351 self.object_to_be_deleted = resource 

1352 

1353 site = model_helpers.get_or_create_site(settings.SITE_ID) 

1354 if site: 1354 ↛ 1360line 1354 didn't jump to line 1360, because the condition on line 1354 was never false

1355 resource.deploy(site, self._prod_deployed_date) 

1356 

1357 # Creation of SiteMembership for production website on ptf_tools is handled in DeployJatsIssue 

1358 # Restoration of SiteMembership is handled in importExtraDataPtfCmd 

1359 

1360 for id_type, id_value in self.xobj.ids: 

1361 if ( 1361 ↛ 1360line 1361 didn't jump to line 1360

1362 id_type != self.provider.pid_type 

1363 and id_type != self.provider.sid_type 

1364 and (id_type != "numdam-id" or self.provider.pid_type != "mathdoc-id") 

1365 ): 

1366 try: 

1367 # May raise Resource.DoesNotExist 

1368 resource_id = ResourceId.objects.get( 

1369 resource=resource, id_type=id_type, id_value=id_value 

1370 ) 

1371 except ResourceId.DoesNotExist: 

1372 resource_id = ResourceId(resource=resource, id_type=id_type, id_value=id_value) 

1373 resource_id.save() 

1374 

1375 for id_type, id_value in self.xobj.extids: 

1376 if ( 1376 ↛ 1375line 1376 didn't jump to line 1375

1377 id_type != self.provider.pid_type 

1378 and id_type != self.provider.sid_type 

1379 and (id_type != "numdam-id" or self.provider.pid_type != "mathdoc-id") 

1380 ): 

1381 try: 

1382 # May raise Resource.DoesNotExist 

1383 ExtId.objects.get(resource=resource, id_type=id_type, id_value=id_value) 

1384 except ExtId.DoesNotExist: 

1385 ext_id = ExtId(resource=resource, id_type=id_type, id_value=id_value) 

1386 ext_id.save() 

1387 

1388 seq = 1 

1389 for a in self.xobj.abstracts: 

1390 value_xml = a["value_xml"] if "value_xml" in a else "" 

1391 value_html = a["value_html"] if "value_html" in a else "" 

1392 value_tex = a["value_tex"] if "value_tex" in a else "" 

1393 

1394 la = Abstract( 

1395 resource=resource, 

1396 tag=a["tag"], 

1397 lang=a["lang"], 

1398 seq=seq, 

1399 value_xml=value_xml, 

1400 value_html=value_html, 

1401 value_tex=value_tex, 

1402 ) 

1403 la.save() 

1404 seq += 1 

1405 

1406 add_contributors(contributors=self.xobj.contributors, resource=resource) 

1407 

1408 for i, kwd in enumerate(self.xobj.kwds): 

1409 k = Kwd( 

1410 resource=resource, lang=kwd["lang"], type=kwd["type"], value=kwd["value"], seq=i 

1411 ) 

1412 k.save() 

1413 

1414 for i, subj in enumerate(self.xobj.subjs): 

1415 s = Subj( 

1416 resource=resource, lang=subj["lang"], type=subj["type"], value=subj["value"], seq=i 

1417 ) 

1418 s.save() 

1419 

1420 seq = 1 

1421 for a in self.xobj.awards: 

1422 abbrev = a["abbrev"] 

1423 award_id = a["award_id"] 

1424 

1425 award = Award(resource=resource, abbrev=abbrev, award_id=award_id, seq=seq) 

1426 award.save() 

1427 seq += 1 

1428 

1429 # TODO custom meta 

1430 

1431 def pre_undo(self): 

1432 super().pre_undo() 

1433 

1434 # None value not detected in check_params (required_delete_params) 

1435 # => undo was already called 

1436 if self.object_to_be_deleted is None: 

1437 raise exceptions.ResourceDoesNotExist("The object to be deleted no longer exists") 

1438 

1439 # Django automatically deletes related objects such as ResourceIds, 

1440 # Abstracts... 

1441 

1442 # Author are deleted by signals (see models.py) 

1443 # Another solution would be to do it here 

1444 

1445 # Undeploy the resource in all sites 

1446 for site in self.object_to_be_deleted.sites.all(): 

1447 self.object_to_be_deleted.undeploy(site) 

1448 

1449 def internal_undo(self): 

1450 super().internal_undo() 

1451 

1452 id = self.object_to_be_deleted.id 

1453 self.object_to_be_deleted.delete() 

1454 

1455 return id 

1456 

1457 def post_undo(self): 

1458 super().internal_undo() 

1459 

1460 self.object_to_be_deleted = None # prevents a 2nd attempt to delete 

1461 

1462 

1463class addPublisherDatabaseCmd(baseCmd): 

1464 """ 

1465 addPublisherDatabaseCmd: adds/remove a publisher 

1466 

1467 Exception raised: 

1468 - ValueError if the init params are empty 

1469 - exceptions.ResourceExists during do if the Publisher already exists 

1470 - exceptions.ResourceDoesNotExist during undo if the Publisher does not exist 

1471 """ 

1472 

1473 def __init__(self, params=None): 

1474 self.xobj = None # model_data object 

1475 self.object_to_be_deleted = None 

1476 

1477 super().__init__(params) 

1478 

1479 self.required_params.extend(["xobj"]) 

1480 self.required_delete_params.append("object_to_be_deleted") 

1481 

1482 def set_object_to_be_deleted(self, obj): 

1483 self.object_to_be_deleted = obj 

1484 

1485 def internal_do(self): 

1486 super().internal_do() 

1487 

1488 # A Publisher is a resource. Therefore it needs a provider (that provides the publisher id/key) 

1489 # As we are creating the key, Mathdoc is the provider 

1490 provider = Provider.objects.get(name="mathdoc", pid_type="mathdoc-id") 

1491 key = model_helpers.make_key(self.xobj.name) 

1492 

1493 try: 

1494 Publisher.objects.get(pub_key=key) 

1495 raise exceptions.ResourceExists("The publisher " + self.xobj.name + " already exists") 

1496 except Publisher.DoesNotExist: 

1497 publisher = Publisher( 

1498 pub_key=key, 

1499 pub_name=self.xobj.name, 

1500 pub_loc=self.xobj.loc, 

1501 pid=key, 

1502 provider=provider, 

1503 ) 

1504 publisher.save() 

1505 

1506 self.object_to_be_deleted = publisher 

1507 return publisher 

1508 

1509 def internal_undo(self): 

1510 super().internal_undo() 

1511 

1512 # None value not detected in check_params (required_delete_params) 

1513 # => undo was already called 

1514 if self.object_to_be_deleted is None: 

1515 raise exceptions.ResourceDoesNotExist("The object to be deleted no longer exists") 

1516 

1517 self.object_to_be_deleted.refresh_from_db() 

1518 

1519 if self.object_to_be_deleted.publishes.count(): 

1520 raise RuntimeError( 

1521 "Impossible de supprimer ce publisher car il a encore des resources qui sont publiées par ce publisher" 

1522 ) 

1523 

1524 id = self.object_to_be_deleted.id 

1525 self.object_to_be_deleted.delete() 

1526 self.object_to_be_deleted = None 

1527 

1528 return id 

1529 

1530 

1531class addContainerDatabaseCmd(addResourceDatabaseCmd): 

1532 """ 

1533 addContainerDatabaseCmd: adds/remove a container 

1534 

1535 an Container needs a Collection 

1536 

1537 Exception raised: 

1538 - ValueError if the init params are empty 

1539 - exceptions.ResourceExists during do if the container already exists 

1540 - exceptions.ResourceDoesNotExist during undo if the Container does not exist 

1541 """ 

1542 

1543 def __init__(self, params=None): 

1544 self.last_modified = None 

1545 self.collection = None 

1546 self._publisher = None 

1547 

1548 super().__init__(params) 

1549 

1550 self.required_params.extend(["collection"]) 

1551 

1552 def add_collection(self, collection): 

1553 if not self.collection: 1553 ↛ exitline 1553 didn't return from function 'add_collection', because the condition on line 1553 was never false

1554 self.collection = collection 

1555 

1556 def pre_do(self): 

1557 super().pre_do() 

1558 

1559 self._publisher = add_publisher(self.xobj) 

1560 

1561 def internal_do(self): 

1562 super().internal_do() 

1563 

1564 vseries = volume = number = seq = "" 

1565 if hasattr(self.xobj, "volume"): 

1566 vseries = self.xobj.vseries 

1567 volume = self.xobj.volume 

1568 number = self.xobj.number 

1569 seq = self.xobj.seq 

1570 elif len(self.xobj.incollection) > 0: 1570 ↛ 1579line 1570 didn't jump to line 1579, because the condition on line 1570 was never false

1571 # Books do not have vseries/volume/number, but a list of incollection 

1572 # Set vseries/volume/number from the 1st incollection (the main collection) 

1573 incol = self.xobj.incollection[0] 

1574 vseries = incol.vseries 

1575 volume = "" 

1576 number = incol.volume 

1577 seq = incol.seq 

1578 

1579 if settings.CONTAINER_SEQUENCED: 1579 ↛ 1580line 1579 didn't jump to line 1580, because the condition on line 1579 was never true

1580 seq = make_int(volume) if volume else make_int(self.xobj.year) 

1581 

1582 last_modified = model_helpers.parse_date_str(self.xobj.last_modified_iso_8601_date_str) 

1583 if self.xobj.prod_deployed_date_iso_8601_date_str: 

1584 self._prod_deployed_date = model_helpers.parse_date_str( 

1585 self.xobj.prod_deployed_date_iso_8601_date_str 

1586 ) 

1587 

1588 with_online_first = ( 

1589 self.xobj.with_online_first if hasattr(self.xobj, "with_online_first") else False 

1590 ) 

1591 

1592 try: 

1593 Container.objects.get( 

1594 pid=self.xobj.pid, 

1595 provider__pid_type=self.provider.pid_type, 

1596 my_collection__pid=self.collection.pid, 

1597 ) 

1598 raise exceptions.ResourceExists("The container " + self.xobj.pid + " already exists") 

1599 

1600 except Container.DoesNotExist: 

1601 container = Container( 

1602 ctype=self.xobj.ctype, 

1603 doi=self.xobj.doi, 

1604 pid=self.xobj.pid, 

1605 lang=self.xobj.lang, 

1606 title_xml=self.xobj.title_xml, 

1607 title_tex=self.xobj.title_tex, 

1608 title_html=self.xobj.title_html, 

1609 trans_lang=self.xobj.trans_lang, 

1610 trans_title_tex=self.xobj.trans_title_tex, 

1611 trans_title_html=self.xobj.trans_title_html, 

1612 provider=self.provider, 

1613 my_publisher=self._publisher, 

1614 my_collection=self.collection, 

1615 year=self.xobj.year, 

1616 vseries=vseries, 

1617 vseries_int=make_int(vseries), 

1618 volume=volume, 

1619 volume_int=make_int(volume), 

1620 number=number, 

1621 number_int=make_int(number), 

1622 seq=seq, 

1623 last_modified=last_modified, 

1624 with_online_first=with_online_first, 

1625 body_html=self.xobj.body_html, 

1626 body_tex=self.xobj.body_tex, 

1627 body_xml=self.xobj.body_xml, 

1628 ) 

1629 

1630 container.save() 

1631 

1632 return container 

1633 

1634 def post_do(self, container): 

1635 super().post_do(container) 

1636 

1637 if hasattr(self.xobj, "incollection"): 

1638 for incol in self.xobj.incollection: 

1639 # Ignore the first incollection which was treated as the main collection 

1640 if incol.pid != self.collection.pid: 

1641 collection = model_helpers.get_collection(incol.pid) 

1642 if collection: 1642 ↛ 1638line 1642 didn't jump to line 1638, because the condition on line 1642 was never false

1643 collection_membership = CollectionMembership( 

1644 collection=collection, 

1645 container=container, 

1646 seq=incol.seq, 

1647 vseries=incol.vseries, 

1648 volume="", 

1649 number=incol.volume, 

1650 vseries_int=make_int(incol.vseries), 

1651 volume_int=0, 

1652 number_int=make_int(incol.volume), 

1653 ) 

1654 collection_membership.save() 

1655 

1656 add_biblio(self.xobj, container) 

1657 add_counts(self.xobj, container, add_total=True) 

1658 add_relations(self.xobj, container) 

1659 add_frontmatter(self.xobj, container) 

1660 

1661 def post_undo(self): 

1662 collection = self.object_to_be_deleted.get_collection() 

1663 super().post_undo() 

1664 

1665 if self._publisher is not None: 

1666 if self._publisher.publishes.count() == 0: 

1667 self._publisher.delete() 

1668 self._publisher = None 

1669 

1670 if collection.parent and collection.content.count() == 0: 

1671 # Child collection that was created on the fly is removed automatically 

1672 # if it no longer has any content 

1673 collection.delete() 

1674 

1675 

1676class addArticleDatabaseCmd(addResourceDatabaseCmd): 

1677 """ 

1678 addArticleDatabaseCmd: adds/remove an article 

1679 

1680 an article needs a container (book or issue) 

1681 

1682 Exception raised: 

1683 - ValueError if the init params are empty 

1684 - exceptions.ResourceExists during do if the article already exists 

1685 - exceptions.ResourceDoesNotExist during undo if the Article does not exist 

1686 """ 

1687 

1688 def __init__(self, params=None): 

1689 # parents are used for book parts. 

1690 self.pseq = 0 # parent seq 

1691 self.parent = None # article parent 

1692 

1693 # Container containing the article 

1694 self.container = None 

1695 self.collection = None 

1696 

1697 self.assign_doi = False 

1698 self.seq = 0 

1699 

1700 self.translated_articles = [] 

1701 

1702 super().__init__(params) 

1703 

1704 def check_params(self): 

1705 super().check_params() 

1706 

1707 check_title = True 

1708 if hasattr(self.xobj, "doi") and self.xobj.doi and self.xobj.doi.find("pcjournal") > 0: 

1709 check_title = False 

1710 if "original_article" in self.required_params: 1710 ↛ 1712line 1710 didn't jump to line 1712, because the condition on line 1710 was never true

1711 # A Translation may not have a title 

1712 check_title = False 

1713 if ( 1713 ↛ 1719line 1713 didn't jump to line 1719

1714 check_title 

1715 and self.is_add_cmd 

1716 and not self.xobj.title_tex 

1717 and not self.xobj.trans_title_tex 

1718 ): 

1719 raise ValueError("title_tex et trans_title_tex sont vides") 

1720 

1721 def set_container(self, container): 

1722 self.container = container 

1723 self.collection = container.my_collection 

1724 self.set_provider(container.provider) 

1725 

1726 def set_collection(self, collection): 

1727 self.collection = collection 

1728 self.set_provider(collection.provider) 

1729 

1730 def set_parent(self, parent): 

1731 self.parent = parent 

1732 

1733 def parse_dates(self): 

1734 dates = { 

1735 "accepted": None, 

1736 "received": None, 

1737 "revised": None, 

1738 "online": None, 

1739 "published": None, 

1740 } 

1741 

1742 for date_entry in self.xobj.history_dates: 

1743 if date_entry["date"] is None: 1743 ↛ 1744line 1743 didn't jump to line 1744, because the condition on line 1743 was never true

1744 raise ValueError( 

1745 f"The date {date_entry['type']} of the article {self.xobj.pid} is None" 

1746 ) 

1747 

1748 date = model_helpers.parse_date_str(date_entry["date"]) 

1749 dates[date_entry["type"]] = date 

1750 

1751 if self.xobj.date_published_iso_8601_date_str: 

1752 dates["published"] = model_helpers.parse_date_str( 

1753 self.xobj.date_published_iso_8601_date_str 

1754 ) 

1755 

1756 if self.xobj.prod_deployed_date_iso_8601_date_str: 

1757 self._prod_deployed_date = model_helpers.parse_date_str( 

1758 self.xobj.prod_deployed_date_iso_8601_date_str 

1759 ) 

1760 

1761 return dates 

1762 

1763 def add_translations(self, xobj, article): 

1764 if hasattr(xobj, "translations") and xobj.translations is not None: 1764 ↛ exitline 1764 didn't return from function 'add_translations', because the condition on line 1764 was never false

1765 for xarticle in xobj.translations: 1765 ↛ 1766line 1765 didn't jump to line 1766, because the loop on line 1765 never started

1766 cmd = addTranslatedArticleDatabaseCmd({"xobj": xarticle}) 

1767 cmd.set_original_article(article) 

1768 cmd.set_provider(article.provider) 

1769 trans_article = cmd.do() 

1770 self.translated_articles.append(trans_article) 

1771 

1772 def internal_do(self): 

1773 super().internal_do() 

1774 

1775 doi = self.xobj.doi 

1776 if self.assign_doi and not doi and self.container: 

1777 colid = self.container.my_collection.pid 

1778 doi = model_helpers.assign_doi(colid) 

1779 

1780 seq = self.xobj.seq or self.seq 

1781 dates = self.parse_dates() 

1782 

1783 try: 

1784 Article.objects.get( 

1785 pid=self.xobj.pid, doi=doi, provider=self.provider, my_container=self.container 

1786 ) 

1787 raise exceptions.ResourceExists("The article " + self.xobj.pid + " already exists") 

1788 except Article.DoesNotExist: 

1789 article = Article( 

1790 pid=self.xobj.pid, 

1791 doi=doi, 

1792 lang=self.xobj.lang, 

1793 title_xml=self.xobj.title_xml, 

1794 title_tex=self.xobj.title_tex, 

1795 title_html=self.xobj.title_html, 

1796 trans_lang=self.xobj.trans_lang, 

1797 trans_title_tex=self.xobj.trans_title_tex, 

1798 trans_title_html=self.xobj.trans_title_html, 

1799 provider=self.provider, 

1800 my_container=self.container, 

1801 fpage=self.xobj.fpage, 

1802 lpage=self.xobj.lpage, 

1803 seq=seq, 

1804 atype=self.xobj.atype, 

1805 page_range=self.xobj.page_range, 

1806 page_type=self.xobj.page_type, 

1807 elocation=self.xobj.elocation, 

1808 article_number=self.xobj.article_number, 

1809 talk_number=self.xobj.talk_number, 

1810 pseq=self.pseq, 

1811 parent=self.parent, 

1812 date_accepted=dates["accepted"], 

1813 date_received=dates["received"], 

1814 date_revised=dates["revised"], 

1815 date_online_first=dates["online"], 

1816 date_published=dates["published"], 

1817 coi_statement=self.xobj.coi_statement, 

1818 funding_statement_html=self.xobj.funding_statement_html, 

1819 funding_statement_xml=self.xobj.funding_statement_xml, 

1820 footnotes_html=self.xobj.footnotes_html, 

1821 footnotes_xml=self.xobj.footnotes_xml, 

1822 body_html=self.xobj.body_html, 

1823 body_tex=self.xobj.body_tex, 

1824 body_xml=self.xobj.body_xml, 

1825 ) 

1826 

1827 article.save() 

1828 

1829 if doi: 

1830 collection = self.collection 

1831 doi_number = model_helpers.get_number_from_doi(doi) 

1832 if doi_number > collection.last_doi: 

1833 collection.last_doi = doi_number 

1834 collection.save() 

1835 

1836 return article 

1837 

1838 def post_do(self, article): 

1839 super().post_do(article) 

1840 

1841 add_biblio(self.xobj, article) 

1842 add_counts(self.xobj, article, add_total=True) 

1843 add_relations(self.xobj, article) 

1844 self.add_translations(self.xobj, article) 

1845 

1846 

1847class addCollectionDatabaseCmd(addResourceDatabaseCmd): 

1848 """ 

1849 addCollectionDatabaseCmd: adds/remove a collection 

1850 

1851 Exception raised: 

1852 - ValueError if the init params are empty 

1853 - exceptions.ResourceExists during do if the book already exists 

1854 - exceptions.ResourceDoesNotExist during undo if the book does not exist 

1855 """ 

1856 

1857 def __init__(self, params=None): 

1858 # self.coltype = None 

1859 # self.wall = 5 

1860 self.parent = None 

1861 

1862 super().__init__(params) 

1863 

1864 def set_parent(self, parent): 

1865 self.parent = parent 

1866 

1867 def internal_do(self): 

1868 super().internal_do() 

1869 

1870 title_sort = ( 

1871 self.xobj.title_tex if len(self.xobj.title_tex) < 129 else self.xobj.title_tex[0:127] 

1872 ) 

1873 

1874 try: 

1875 col = Collection.objects.get( 

1876 pid=self.xobj.pid, 

1877 title_tex=self.xobj.title_tex, 

1878 provider__pid_type=self.provider.pid_type, 

1879 ) 

1880 except Collection.DoesNotExist: 

1881 col = Collection.objects.create( 

1882 coltype=self.xobj.coltype, 

1883 abbrev=self.xobj.abbrev, 

1884 wall=self.xobj.wall, 

1885 pid=self.xobj.pid, 

1886 lang=self.xobj.lang, 

1887 title_xml=self.xobj.title_xml, 

1888 title_tex=self.xobj.title_tex, 

1889 title_html=self.xobj.title_html, 

1890 title_sort=title_sort, 

1891 trans_lang=self.xobj.trans_lang, 

1892 trans_title_tex=self.xobj.trans_title_tex, 

1893 trans_title_html=self.xobj.trans_title_html, 

1894 provider=self.provider, 

1895 parent=self.parent, 

1896 ) 

1897 else: 

1898 raise exceptions.ResourceExists(f"The collection pid:{self.xobj.pid} already exists") 

1899 return col 

1900 

1901 # def internal_undo(self): 

1902 # super().internal_undo() 

1903 # 

1904 # try: 

1905 # col = Collection.objects.get(pid=self.pid, 

1906 # provider__pid_type=self.provider.pid_type) 

1907 # id = col.id 

1908 # if col.parent and not col.ancestors.all().count() : 

1909 # col.parent = None 

1910 # col.delete() 

1911 # 

1912 # 

1913 # elif not col.parent and not col.ancestors.all().count() : 

1914 # col.delete() 

1915 # 

1916 # except Collection.DoesNotExist: 

1917 # raise exceptions.ResourceDoesNotExist( 

1918 # "The collection " + self.pid + " does not exist") 

1919 # 

1920 # return id 

1921 

1922 

1923class addTranslatedArticleDatabaseCmd(addArticleDatabaseCmd): 

1924 """ 

1925 addTranslatedArticleDatabaseCmd: adds/remove a translated article 

1926 """ 

1927 

1928 def __init__(self, params=None): 

1929 super().__init__(params) 

1930 

1931 self.original_article = None 

1932 

1933 self.required_params = ["xobj", "original_article"] 

1934 

1935 def set_original_article(self, original_article): 

1936 self.original_article = original_article 

1937 

1938 def internal_do(self): 

1939 # DO NOT CALL the parent internal_do or it will create an Article 

1940 

1941 dates = self.parse_dates() 

1942 

1943 try: 

1944 TranslatedArticle.objects.get(pid=self.xobj.pid) 

1945 raise exceptions.ResourceExists( 

1946 "The translated article " + self.xobj.pid + " already exists" 

1947 ) 

1948 except Article.DoesNotExist: 

1949 article = TranslatedArticle( 

1950 original_article=self.original_article, 

1951 pid=self.xobj.pid, 

1952 doi=self.xobj.doi, 

1953 lang=self.xobj.lang, 

1954 title_xml=self.xobj.title_xml, 

1955 title_tex=self.xobj.title_tex, 

1956 title_html=self.xobj.title_html, 

1957 trans_lang=self.xobj.trans_lang, 

1958 trans_title_tex=self.xobj.trans_title_tex, 

1959 trans_title_html=self.xobj.trans_title_html, 

1960 provider=self.provider, 

1961 my_container=None, 

1962 fpage=self.xobj.fpage, 

1963 lpage=self.xobj.lpage, 

1964 seq=self.xobj.seq, 

1965 atype=self.xobj.atype, 

1966 page_range=self.xobj.page_range, 

1967 page_type=self.xobj.page_type, 

1968 elocation=self.xobj.elocation, 

1969 article_number=self.xobj.article_number, 

1970 talk_number=self.xobj.talk_number, 

1971 pseq=self.pseq, 

1972 parent=self.parent, 

1973 date_accepted=dates["accepted"], 

1974 date_received=dates["received"], 

1975 date_revised=dates["revised"], 

1976 date_online_first=dates["online"], 

1977 date_published=dates["published"], 

1978 coi_statement=self.xobj.coi_statement, 

1979 funding_statement_html=self.xobj.funding_statement_html, 

1980 funding_statement_xml=self.xobj.funding_statement_xml, 

1981 footnotes_html=self.xobj.footnotes_html, 

1982 footnotes_xml=self.xobj.footnotes_xml, 

1983 body_html=self.xobj.body_html, 

1984 body_tex=self.xobj.body_tex, 

1985 body_xml=self.xobj.body_xml, 

1986 ) 

1987 

1988 article.save() 

1989 

1990 return article 

1991 

1992 def post_do(self, container): 

1993 super().post_do(container) 

1994 

1995 if hasattr(self.xobj, "incollection"): 

1996 for incol in self.xobj.incollection: 

1997 # Ignore the first incollection which was treated as the main collection 

1998 if incol.pid != self.collection.pid: 

1999 collection = model_helpers.get_collection(incol.pid) 

2000 if collection: 

2001 collection_membership = CollectionMembership( 

2002 collection=collection, 

2003 container=container, 

2004 seq=incol.seq, 

2005 vseries=incol.vseries, 

2006 volume="", 

2007 number=incol.volume, 

2008 vseries_int=make_int(incol.vseries), 

2009 volume_int=0, 

2010 number_int=make_int(incol.volume), 

2011 ) 

2012 collection_membership.save() 

2013 

2014 add_biblio(self.xobj, container) 

2015 add_counts(self.xobj, container, add_total=True) 

2016 add_relations(self.xobj, container) 

2017 add_frontmatter(self.xobj, container) 

2018 

2019 def post_undo(self): 

2020 collection = self.object_to_be_deleted.get_collection() 

2021 super().post_undo() 

2022 

2023 if self._publisher is not None: 

2024 if self._publisher.publishes.count() == 0: 

2025 self._publisher.delete() 

2026 self._publisher = None 

2027 

2028 if collection.parent and collection.content.count() == 0: 

2029 # Child collection that was created on the fly is removed automatically 

2030 # if it no longer has any content 

2031 collection.delete() 

2032 

2033 

2034########################################################################## 

2035########################################################################## 

2036# 

2037# Update Commands 

2038# 

2039########################################################################## 

2040########################################################################## 

2041 

2042 

2043class updateCollectionDatabaseCmd(addResourceDatabaseCmd): 

2044 """ 

2045 updateCollectionDatabaseCmd: updates a Collection (journal, acta) 

2046 

2047 a Collection needs a Provider 

2048 

2049 Exception raised: 

2050 - ValueError if the init params are empty 

2051 - exceptions.ResourceDoesNotExist during do if the Collection does not exist 

2052 """ 

2053 

2054 def __init__(self, params=None): 

2055 super().__init__(params) 

2056 

2057 def internal_do(self): 

2058 super().internal_do() 

2059 

2060 try: 

2061 collection = Collection.objects.get( 

2062 pid=self.xobj.pid, provider__pid_type=self.provider.pid_type 

2063 ) 

2064 except Collection.DoesNotExist: 

2065 raise exceptions.ResourceDoesNotExist(f"The journal {self.xobj.pid} does not exist") 

2066 

2067 # delete objects in direct relation with the collection 

2068 # the new related objects (extlink, resourceid...) will be added in 

2069 # addResourceDatabaseCmd::post_do 

2070 collection.extlink_set.all().delete() 

2071 collection.resourceid_set.all().delete() 

2072 collection.abstract_set.all().delete() 

2073 

2074 title_sort = ( 

2075 self.xobj.title_tex if len(self.xobj.title_tex) < 129 else self.xobj.title_tex[0:127] 

2076 ) 

2077 

2078 collection.coltype = self.xobj.coltype 

2079 collection.lang = self.xobj.lang 

2080 collection.title_xml = self.xobj.title_xml 

2081 collection.title_tex = self.xobj.title_tex 

2082 collection.title_html = self.xobj.title_html 

2083 collection.title_sort = title_sort 

2084 collection.abbrev = self.xobj.abbrev 

2085 

2086 collection.wall = self.xobj.wall 

2087 collection.save() 

2088 

2089 return collection 

2090 

2091 

2092class updateExtLinkDatabaseCmd(baseCmd): 

2093 """ 

2094 An extlink is a link to an external object (website, image...) 

2095 updateExtLinkDatabaseCmd: 

2096 - updates an ExtLink, or 

2097 - creates the object if it does not exist, or 

2098 - deletes the object if it exists and the new location value is empty 

2099 

2100 Exception raised: 

2101 - ValueError if the init params are empty 

2102 """ 

2103 

2104 def __init__(self, params=None): 

2105 self.rel = None # 'website' or 'small_icon' 

2106 self.mimetype = None 

2107 self.location = None 

2108 self.metadata = None 

2109 self.seq = None 

2110 self.resource = None 

2111 self.base = None 

2112 

2113 super().__init__(params) 

2114 

2115 self.required_params.extend(["rel", "resource"]) 

2116 

2117 def set_resource(self, resource): 

2118 self.resource = resource 

2119 

2120 def set_base(self, base): 

2121 self.base = base 

2122 

2123 def internal_do(self): 

2124 super().internal_do() 

2125 

2126 extlink = None 

2127 

2128 try: 

2129 extlink = ExtLink.objects.get(resource=self.resource, rel=self.rel) 

2130 self.seq = extlink.seq 

2131 

2132 except ExtLink.DoesNotExist: 

2133 if self.location: 

2134 if not self.seq: 

2135 self.seq = ExtLink.objects.filter(resource=self.resource).count() + 1 

2136 

2137 extlink = ExtLink(resource=self.resource, rel=self.rel, seq=self.seq) 

2138 extlink.save() 

2139 

2140 if self.location: 

2141 extlink.location = self.location 

2142 

2143 if self.rel in ["website", "test_website"]: 

2144 self.metadata = "website" 

2145 

2146 if self.base: 

2147 extlink.base = self.base 

2148 if self.mimetype: 

2149 extlink.mimetype = self.mimetype 

2150 if self.metadata: 

2151 extlink.metadata = self.metadata 

2152 

2153 extlink.seq = self.seq 

2154 

2155 extlink.save() 

2156 elif extlink: 

2157 extlink.delete() 

2158 extlink = None 

2159 

2160 return extlink 

2161 

2162 

2163class updateResourceIdDatabaseCmd(baseCmd): 

2164 """ 

2165 A ResourceId is another id of the resource (doi, issn...) 

2166 updateResourceIdDatabaseCmd: 

2167 - updates an ResourceId, or 

2168 - creates the object if it does not exist, or 

2169 - deletes the object if it exists and the new location value is empty 

2170 

2171 Exception raised: 

2172 - ValueError if the init params are empty 

2173 """ 

2174 

2175 def __init__(self, params={}): 

2176 self.id_type = None # 'doi', 'issn', 'e-issn' 

2177 self.id_value = None 

2178 self.resource = None 

2179 

2180 super().__init__(params) 

2181 

2182 self.required_params.extend(["id_type", "resource"]) 

2183 

2184 def set_resource(self, resource): 

2185 self.resource = resource 

2186 

2187 def internal_do(self): 

2188 super().internal_do() 

2189 

2190 resourceid = None 

2191 id_type = self.id_type 

2192 id_value = self.id_value 

2193 

2194 if id_type == "ojs-id": 2194 ↛ 2209line 2194 didn't jump to line 2209, because the condition on line 2194 was never false

2195 # ojs-id (aka article folder name in /cedram_dev) may not be unique 

2196 # create an internal id 

2197 if id_value == "edito": 2197 ↛ 2198line 2197 didn't jump to line 2198, because the condition on line 2197 was never true

2198 qs = ResourceId.objects.filter( 

2199 id_type="ojs-id", id_value__startswith=id_value + "$$" 

2200 ) 

2201 count = qs.count() 

2202 if count: 

2203 id_value = id_value + "$$" + str(count + 1) 

2204 else: 

2205 qs = ResourceId.objects.filter(id_type="ojs-id", id_value__startswith=id_value) 

2206 count = qs.count() 

2207 if count: 2207 ↛ 2208line 2207 didn't jump to line 2208, because the condition on line 2207 was never true

2208 id_value = id_value + "$$" + str(count + 1) 

2209 try: 

2210 resourceid = ResourceId.objects.get(resource=self.resource, id_type=id_type) 

2211 

2212 except ResourceId.DoesNotExist: 

2213 if id_value: 2213 ↛ 2216line 2213 didn't jump to line 2216, because the condition on line 2213 was never false

2214 resourceid = ResourceId(resource=self.resource, id_type=id_type, id_value=id_value) 

2215 

2216 if id_value: 2216 ↛ 2219line 2216 didn't jump to line 2219, because the condition on line 2216 was never false

2217 resourceid.id_value = id_value 

2218 resourceid.save() 

2219 elif resourceid: 

2220 resourceid.delete() 

2221 resourceid = None 

2222 

2223 return resourceid 

2224 

2225 

2226class deleteResourceDatabaseCmd(baseCmd): 

2227 """ 

2228 deleteResourceDatabaseCmd: base class for all resources 

2229 NOT USED. TODO: remove 

2230 """ 

2231 

2232 def __init__(self, params=None): 

2233 self.pid = None # primary id given by the provider 

2234 self.sid = None # surrogate id given by the provider 

2235 self.provider = None 

2236 

2237 super().__init__(params) 

2238 

2239 def pre_do(self): 

2240 super().pre_do() 

2241 

2242 if self.pid is None and self.sid is None: 

2243 raise ValueError("pid et sid sont vides") 

2244 

2245 if self.provider is None: 

2246 raise ValueError("provider est vide") 

2247 

2248 def set_provider(self, provider): 

2249 self.provider = provider 

2250 

2251 # May raise Provider.DoesNotExist 

2252 def set_provider_by_name_or_id(self, provider_name="", pid_type="", sid_type=None): 

2253 self.provider = Provider.objects.get( 

2254 name=provider_name, pid_type=pid_type, sid_type=sid_type 

2255 ) 

2256 

2257 

2258class publishArticleDatabaseCmd(baseCmd): 

2259 """ 

2260 Publish an article <=> Create a pub-date 

2261 @return: list of updated articles 

2262 """ 

2263 

2264 def __init__(self, params=None): 

2265 self.article = None 

2266 self.container = None 

2267 self.pre_publish = False # Publish on the test website 

2268 self.update_db = True 

2269 super().__init__(params) 

2270 

2271 self.required_params.extend(["article"]) 

2272 

2273 def set_article(self, article): 

2274 self.article = article 

2275 self.container = article.my_container 

2276 if self.container is None and article.classname == "TranslatedArticle": 

2277 self.container = self.article.original_article.my_container 

2278 

2279 def publish_article(self, article, update_articles): 

2280 # In a collection with standalone articles, the article number is based on 

2281 # the publication order. 

2282 # It has to be updated when we publish because of the articles excluded for publication 

2283 update_article_number = False 

2284 # TODO: use a param instead of checking PCJ 

2285 if self.container.my_collection.pid == "PCJ" and not self.pre_publish: 2285 ↛ 2286line 2285 didn't jump to line 2286, because the condition on line 2285 was never true

2286 update_article_number = True 

2287 

2288 article_number = ( 

2289 self.container.article_set.filter(date_published__isnull=False).count() + 1 

2290 ) 

2291 

2292 if (not article.date_published and not self.pre_publish) or ( 2292 ↛ exitline 2292 didn't return from function 'publish_article', because the condition on line 2292 was never false

2293 not article.date_pre_published and self.pre_publish 

2294 ): 

2295 today = model_helpers.parse_date_str(timezone.now().isoformat()) 

2296 

2297 # Les articles numérisés n'ont pas à avoir de date_published 

2298 # Les anciens articles issus de Cedrics ont déjà une date_published. 

2299 # Les nouveaux articles du Centre Mersenne sont publiés à partir de 2019. 

2300 year = article.get_year() 

2301 fyear, lyear = model_helpers.get_first_last_years(year) 

2302 try: 

2303 fyear = int(fyear) 

2304 except ValueError: 

2305 fyear = 0 

2306 

2307 if fyear > 2016 and not self.container.with_online_first: 

2308 if self.pre_publish: 2308 ↛ 2309line 2308 didn't jump to line 2309, because the condition on line 2308 was never true

2309 article.date_pre_published = today 

2310 else: 

2311 article.date_published = today 

2312 if update_article_number: 2312 ↛ 2313line 2312 didn't jump to line 2313, because the condition on line 2312 was never true

2313 article.article_number = "e" + str(article_number) 

2314 article_number += 1 

2315 if self.update_db: 2315 ↛ 2317line 2315 didn't jump to line 2317, because the condition on line 2315 was never false

2316 article.save() 

2317 update_articles.append(article) 

2318 elif fyear == 0 or self.container.with_online_first: 2318 ↛ 2319line 2318 didn't jump to line 2319, because the condition on line 2318 was never true

2319 if not article.date_online_first and not self.pre_publish: 

2320 # Online First 

2321 article.date_online_first = today 

2322 if self.update_db: 

2323 article.save() 

2324 update_articles.append(article) 

2325 elif self.pre_publish: 

2326 article.date_pre_published = today 

2327 if self.update_db: 

2328 article.save() 

2329 update_articles.append(article) 

2330 

2331 def internal_do(self): 

2332 update_articles = [] 

2333 

2334 # In a collection with standalone articles, the article number is based on 

2335 # the publication order. 

2336 # It has to be updated when we publish because of the articles excluded for publication 

2337 update_article_number = False 

2338 # TODO: use a param instead of checking PCJ 

2339 if self.container.my_collection.pid == "PCJ" and not self.pre_publish: 

2340 update_article_number = True 

2341 

2342 self.publish_article(self.article, update_articles) 

2343 

2344 # TODO: update in upload articles ? 

2345 

2346 # PCJ: update the article seq, it is used by the breadcrumb 

2347 if update_article_number: 

2348 i = 1 

2349 for article in self.container.article_set.exclude(do_not_publish=True).order_by( 

2350 "-date_published" 

2351 ): 

2352 article.seq = i 

2353 i += 1 

2354 article.save() 

2355 

2356 return update_articles 

2357 

2358 

2359class publishContainerDatabaseCmd(publishArticleDatabaseCmd): 

2360 """ 

2361 Publish a container <=> Create a pub-date for all articles/book-parts of the container 

2362 @var : fake : if True, return list of potentially updated articles BUT don't update database 

2363 @return: list of updated articles 

2364 """ 

2365 

2366 def __init__(self, params=None): 

2367 super().__init__(params) 

2368 

2369 self.required_params = ["container"] 

2370 

2371 def set_container(self, container): 

2372 self.container = container 

2373 

2374 def internal_do(self): 

2375 update_articles = [] 

2376 

2377 # In a collection with standalone articles, the article number is based on 

2378 # the publication order. 

2379 # It has to be updated when we publish because of the articles excluded for publication 

2380 update_article_number = False 

2381 # TODO: use a param instead of checking PCJ 

2382 if self.container.my_collection.pid == "PCJ" and not self.pre_publish: 2382 ↛ 2383line 2382 didn't jump to line 2383, because the condition on line 2382 was never true

2383 update_article_number = True 

2384 

2385 for article in self.container.article_set.exclude(do_not_publish=True): 

2386 self.publish_article(article, update_articles) 

2387 

2388 # PCJ: update the article seq, it is used by the breadcrumb 

2389 if update_article_number: 2389 ↛ 2390line 2389 didn't jump to line 2390, because the condition on line 2389 was never true

2390 i = 1 

2391 for article in self.container.article_set.exclude(do_not_publish=True).order_by( 

2392 "-date_published" 

2393 ): 

2394 article.seq = i 

2395 i += 1 

2396 article.save() 

2397 

2398 return update_articles 

2399 

2400 # TODO Le container(book) peut également avoir une date de publication 

2401 

2402 

2403def convert_contribs(pid, delete=False): 

2404 if pid == "PCJ": 

2405 # PCJ: pid start with "10_24072" 

2406 pid = "10_24072" 

2407 

2408 if delete: 

2409 revert_contribs(pid) 

2410 

2411 if pid is not None: 

2412 qs = Contrib.objects.filter( 

2413 Q(group__resource__pid__startswith=pid) 

2414 | Q(group__bibitem__resource__pid__startswith=pid) 

2415 ) 

2416 else: 

2417 qs = Contrib.objects.all()[0:100] 

2418 

2419 total = qs.count() 

2420 i = 1 

2421 percent = 0 

2422 

2423 for contrib in qs: 

2424 if int(i / total * 100) > percent: 

2425 percent += 1 

2426 if percent % 5 == 0: 

2427 print(pid, percent) 

2428 i += 1 

2429 

2430 # contrib_fields_list = Contrib.get_fields_list() 

2431 # contrib_fields = { 

2432 # attr_name: getattr(contrib, attr_name) for attr_name in contrib_fields_list 

2433 # } 

2434 

2435 # Augment the contrib attributes to match Contribution fields 

2436 role = contrib.contrib_type 

2437 if not role: 

2438 role = contrib.group.content_type 

2439 if not role: 

2440 role = "author" 

2441 if role[-1] == "s": 

2442 role = role[0:-1] 

2443 contrib.corresponding = role == "corresponding" 

2444 contrib.role = role if role != "corresponding" else "author" 

2445 

2446 ref_name = contrib.last_name if contrib.last_name else None 

2447 if ref_name is None: 

2448 ref_name = contrib.string_name if contrib.string_name else None 

2449 letter = get_first_letter(ref_name) if ref_name else "" 

2450 contrib.first_letter = letter 

2451 

2452 contrib.deceased_before_publication = contrib.deceased 

2453 contrib.idref = None 

2454 contrib.mid = None 

2455 

2456 contribution_fields_list = Contribution.get_fields_list() 

2457 contributions_fields = { 

2458 attr_name: getattr(contrib, attr_name) for attr_name in contribution_fields_list 

2459 } 

2460 

2461 contribution = Contribution( 

2462 resource=contrib.group.resource, bibitem=contrib.group.bibitem, **contributions_fields 

2463 ) 

2464 contribution.save() 

2465 

2466 for contrib_address in contrib.contribaddress_set.all(): 

2467 contrib_address.contribution = contribution 

2468 contrib_address.save() 

2469 

2470 qs = Contribution.objects.filter( 

2471 Q(resource__pid__startswith=pid) | Q(bibitem__resource__pid__startswith=pid) 

2472 ) 

2473 print(qs.count()) 

2474 

2475 

2476def revert_contribs(pid): 

2477 print(f"Delete Contribution for {pid}", end=" ") 

2478 Contribution.objects.filter( 

2479 Q(resource__pid__startswith=pid) | Q(bibitem__resource__pid__startswith=pid) 

2480 ).delete() 

2481 print("done")