Coverage for src/ptf/cmds/database_cmds.py: 58%

1178 statements  

« prev     ^ index     » next       coverage.py v7.6.4, created at 2024-11-05 09:56 +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 ResourceInSpecialIssue 

37from ..models import SiteMembership 

38from ..models import Stats 

39from ..models import Subj 

40from ..models import SupplementaryMaterial 

41from ..models import TranslatedArticle 

42from ..models import XmlBase 

43from ..models import parse_page_count 

44from .base_cmds import baseCmd 

45from .base_cmds import make_int 

46 

47# from .xml.jats import jats_parser 

48 

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

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

51# Example: number and vseries for a Container 

52 

53 

54def get_first_letter(name): 

55 letter = "" 

56 if name: 

57 letter = name[0] 

58 letter = unidecode(letter) 

59 if len(letter) > 1: 

60 letter = letter[0] 

61 letter = letter.upper() 

62 if letter == "?": 

63 letter = "Z" 

64 

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

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

67 letter = name[3].upper() 

68 

69 return letter 

70 

71 

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

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

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

75 

76 seq = 0 

77 for contrib in contributors: 

78 seq += 1 

79 

80 contrib["seq"] = seq 

81 contribution_fields_list = Contribution.get_fields_list() 

82 contributions_fields = { 

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

84 } 

85 

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

87 contribution.save() 

88 

89 for addr in contrib["addresses"]: 

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

91 contrib_address.save() 

92 

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

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

95 try: 

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

97 author.count += 1 

98 except Author.DoesNotExist: 

99 letter = ref_name[0] 

100 letter = unidecode(letter) 

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

102 letter = letter[0] 

103 letter = letter.upper() 

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

105 letter = "Z" 

106 

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

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

109 letter = ref_name[3].upper() 

110 

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

112 author.save() 

113 

114 

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

116 seq = 1 

117 for name, value in xobj.counts: 

118 try: 

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

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

121 except ResourceCount.DoesNotExist: 

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

123 

124 resource_count.save() 

125 

126 if name == "page-count" and add_total: 126 ↛ 137line 126 didn't jump to line 137 because the condition on line 126 was always true

127 value = parse_page_count(value) 

128 

129 try: 

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

131 total.value += value 

132 except Stats.DoesNotExist: 

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

134 

135 total.save() 

136 

137 seq += 1 

138 

139 

140def add_biblio(xobj, resource): 

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

142 bibitem = BibItem( 

143 resource=resource, 

144 sequence=seq, 

145 label=xbibitem.label, 

146 citation_xml=xbibitem.citation_xml, 

147 citation_tex=xbibitem.citation_tex, 

148 citation_html=xbibitem.citation_html, 

149 type=xbibitem.type, 

150 user_id=xbibitem.user_id, 

151 article_title_tex=xbibitem.article_title_tex, 

152 chapter_title_tex=xbibitem.chapter_title_tex, 

153 source_tex=xbibitem.source_tex, 

154 publisher_name=xbibitem.publisher_name, 

155 publisher_loc=xbibitem.publisher_loc, 

156 institution=xbibitem.institution, 

157 series=xbibitem.series, 

158 volume=xbibitem.volume, 

159 issue=xbibitem.issue, 

160 month=xbibitem.month, 

161 year=xbibitem.year, 

162 comment=xbibitem.comment, 

163 annotation=xbibitem.annotation, 

164 fpage=xbibitem.fpage, 

165 lpage=xbibitem.lpage, 

166 page_range=xbibitem.page_range, 

167 size=xbibitem.size, 

168 ) 

169 

170 bibitem.save() 

171 

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

173 

174 if xbibitem.extids: 

175 the_types = [] 

176 for id_type, id_value in xbibitem.extids: 

177 if id_type and id_type not in the_types: 

178 the_types.append(id_type) 

179 

180 bibitemid = BibItemId( 

181 bibitem=bibitem, 

182 id_type=id_type, 

183 id_value=id_value, 

184 checked=True, 

185 false_positive=False, 

186 ) 

187 

188 bibitemid.save() 

189 

190 

191def add_relations(xobj, resource): 

192 for relation in xobj.relations: 

193 with_doi = "10." in relation.id_value 

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

195 raise ValueError( 

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

197 ) 

198 

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

200 name = relation.rel_type 

201 relationname = model_helpers.get_relationname_left(left_name=name) 

202 

203 if relationname: 

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

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

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

207 # Example <article> 

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

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

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

211 

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

213 subject_resource = resource 

214 object_pid = relation.id_value 

215 # Find the resource to update the related article 

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

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

218 if with_doi: 218 ↛ 219line 218 didn't jump to line 219 because the condition on line 218 was never true

219 object_resource = model_helpers.get_article_by_doi(relation.id_value) 

220 else: 

221 object_resource = model_helpers.get_article(relation.id_value) 

222 else: 

223 relationname = model_helpers.get_relationname_right(right_name=name) 

224 

225 if relationname: 225 ↛ 247line 225 didn't jump to line 247 because the condition on line 225 was always true

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

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

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

229 # Example <article> 

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

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

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

233 

234 subject_pid = relation.id_value 

235 # Find the resource to update the related article 

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

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

238 if with_doi: 238 ↛ 239line 238 didn't jump to line 239 because the condition on line 238 was never true

239 subject_resource = model_helpers.get_article_by_doi(relation.id_value) 

240 else: 

241 subject_resource = model_helpers.get_article(relation.id_value) 

242 

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

244 object_resource = resource 

245 

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

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

248 relationname = RelationName( 

249 left="refers to", 

250 right="is referenced by", 

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

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

253 ) 

254 relationname.save() 

255 

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

257 subject_resource = resource 

258 object_pid = relation.id_value 

259 # Find the resource to update the related article 

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

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

262 if with_doi: 

263 object_resource = model_helpers.get_article_by_doi(relation.id_value) 

264 else: 

265 object_resource = model_helpers.get_article(relation.id_value) 

266 

267 if relationname: 267 ↛ 192line 267 didn't jump to line 192 because the condition on line 267 was always true

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

269 cmd = addRelationshipDatabaseCmd(params) 

270 if subject_resource is not None: 

271 cmd.set_subject_resource(subject_resource) 

272 if object_resource is not None: 

273 cmd.set_object_resource(object_resource) 

274 cmd.set_relationname(relationname) 

275 

276 try: 

277 relationship = Relationship.objects.get( 

278 subject_pid=subject_pid, object_pid=object_pid, rel_info=relationname 

279 ) 

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

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

282 # RelationShip 

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

284 raise exceptions.ResourceExists( 

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

286 ) 

287 if subject_resource is not None: 287 ↛ 289line 287 didn't jump to line 289 because the condition on line 287 was always true

288 relationship.resource = subject_resource 

289 if object_resource is not None: 289 ↛ 291line 289 didn't jump to line 291 because the condition on line 289 was always true

290 relationship.related = object_resource 

291 relationship.save() 

292 

293 except Relationship.DoesNotExist: 

294 relationship = Relationship( 

295 subject_pid=subject_pid, 

296 object_pid=object_pid, 

297 resource=subject_resource, 

298 related=object_resource, 

299 rel_info=relationname, 

300 ) 

301 relationship.save() 

302 

303 

304def add_frontmatter(xobj, resource): 

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

306 frontmatter = FrontMatter( 

307 resource=resource, 

308 value_xml=xobj.frontmatter_xml, 

309 value_html=xobj.frontmatter_toc_html, 

310 foreword_html=xobj.frontmatter_foreword_html, 

311 ) 

312 frontmatter.save() 

313 

314 

315def add_publisher(xobj): 

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

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

318 

319 if publisher is None: 

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

321 publisher = cmd.do() 

322 

323 return publisher 

324 

325 

326class addSiteDatabaseCmd(baseCmd): 

327 """ 

328 addSiteDatabaseCmd: adds/remove a PtfSite 

329 

330 Exception raised: 

331 - ValueError if the init params are empty 

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

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

334 - RuntimeError during undo if resources are still deployed 

335 """ 

336 

337 def __init__(self, params=None): 

338 self.site_name = None 

339 self.site_domain = None 

340 self.site_id = None 

341 

342 super().__init__(params) 

343 

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

345 self.site_name = settings.SITE_NAME 

346 

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

348 self.site_domain = settings.SITE_DOMAIN 

349 

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

351 

352 # Returns a PtfSite 

353 def internal_do(self) -> PtfSite: 

354 super().internal_do() 

355 

356 try: 

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

358 raise exceptions.ResourceExists( 

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

360 ) 

361 except PtfSite.DoesNotExist: 

362 site = PtfSite( 

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

364 ) 

365 site.save() 

366 

367 return site 

368 

369 def pre_undo(self): 

370 super().pre_undo() 

371 

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

373 raise RuntimeError( 

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

375 ) 

376 

377 def internal_undo(self): 

378 super().internal_undo() 

379 

380 try: 

381 # may throw PtfSite.DoesNotExist 

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

383 id = site.id 

384 site.delete() 

385 except PtfSite.DoesNotExist: 

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

387 

388 return id 

389 

390 

391class addProviderDatabaseCmd(baseCmd): 

392 """ 

393 addProviderDatabaseCmd: adds/remove a Provider 

394 

395 Exception raised: 

396 - ValueError if the init params are empty 

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

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

399 """ 

400 

401 def __init__(self, params=None): 

402 self.name = None 

403 self.pid_type = None 

404 self.sid_type = None 

405 

406 super().__init__(params) 

407 

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

409 

410 def internal_do(self): 

411 super().internal_do() 

412 

413 try: 

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

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

416 except Provider.DoesNotExist: 

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

418 p.save() 

419 

420 return p 

421 

422 def internal_undo(self): 

423 super().internal_undo() 

424 

425 try: 

426 p = Provider.objects.get( 

427 name=self.name, pid_type=self.pid_type, sid_type=self.sid_type 

428 ) 

429 id = p.id 

430 p.delete() 

431 except Provider.DoesNotExist: 

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

433 

434 return id 

435 

436 

437class addXmlBaseDatabaseCmd(baseCmd): 

438 """ 

439 addXmlBaseDatabaseCmd: adds/remove an XmlBase 

440 

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

442 

443 Exception raised: 

444 - ValueError if the init params are empty 

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

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

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

448 """ 

449 

450 def __init__(self, params=None): 

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

452 

453 super().__init__(params) 

454 

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

456 

457 def internal_do(self): 

458 super().internal_do() 

459 

460 try: 

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

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

463 except XmlBase.DoesNotExist: 

464 xmlbase = XmlBase(base=self.base) 

465 xmlbase.save() 

466 

467 return xmlbase 

468 

469 def internal_undo(self): 

470 super().internal_undo() 

471 

472 try: 

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

474 id = xmlbase.id 

475 except XmlBase.DoesNotExist: 

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

477 

478 try: 

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

480 

481 if extlink: 

482 raise RuntimeError( 

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

484 ) 

485 

486 except ExtLink.DoesNotExist: 

487 pass 

488 

489 try: 

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

491 

492 if related_object: 

493 raise RuntimeError( 

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

495 ) 

496 

497 except RelatedObject.DoesNotExist: 

498 pass 

499 

500 xmlbase.delete() 

501 return id 

502 

503 

504class addExtLinkDatabaseCmd(baseCmd): 

505 """ 

506 addExtLinkDatabaseCmd: adds/remove an ExtLink 

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

508 

509 Exception raised: 

510 - ValueError if the init params are empty 

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

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

513 - RuntimeError during undo if resources are still deployed 

514 """ 

515 

516 def __init__(self, params=None): 

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

518 self.mimetype = None 

519 self.location = None 

520 self.metadata = None 

521 self.seq = None 

522 self.resource = None 

523 self.base = None 

524 

525 super().__init__(params) 

526 

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

528 

529 def set_resource(self, resource): 

530 self.resource = resource 

531 

532 def set_base(self, base): 

533 self.base = base 

534 

535 def internal_do(self): 

536 super().internal_do() 

537 

538 try: 

539 ExtLink.objects.get( 

540 resource=self.resource, 

541 base=self.base, 

542 rel=self.rel, 

543 mimetype=self.mimetype, 

544 location=self.location, 

545 metadata=self.metadata, 

546 seq=self.seq, 

547 ) 

548 raise exceptions.ResourceExists( 

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

550 ) 

551 except ExtLink.DoesNotExist: 

552 extlink = ExtLink( 

553 resource=self.resource, 

554 base=self.base, 

555 rel=self.rel, 

556 mimetype=self.mimetype, 

557 location=self.location, 

558 metadata=self.metadata, 

559 seq=self.seq, 

560 ) 

561 extlink.save() 

562 

563 return extlink 

564 

565 def internal_undo(self): 

566 super().internal_undo() 

567 

568 try: 

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

570 id = extlink.id 

571 extlink.delete() 

572 except ExtLink.DoesNotExist: 

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

574 

575 return id 

576 

577 

578class addExtIdDatabaseCmd(baseCmd): 

579 """ 

580 addExtIdDatabaseCmd: adds/remove an ExtId 

581 

582 Exception raised: 

583 - ValueError if the init params are empty 

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

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

586 - RuntimeError during undo if resources are still deployed 

587 """ 

588 

589 def __init__(self, params=None): 

590 self.resource = None 

591 self.id_type = None 

592 self.id_value = None 

593 self.checked = True 

594 self.false_positive = False 

595 

596 super().__init__(params) 

597 

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

599 

600 def set_resource(self, resource): 

601 self.resource = resource 

602 

603 def internal_do(self): 

604 super().internal_do() 

605 

606 try: 

607 # May raise Resource.DoesNotExist 

608 extid = ExtId.objects.get( 

609 resource=self.resource, id_type=self.id_type, id_value=self.id_value 

610 ) 

611 except ExtId.DoesNotExist: 

612 extid = ExtId( 

613 resource=self.resource, 

614 id_type=self.id_type, 

615 id_value=self.id_value, 

616 checked=self.checked, 

617 false_positive=self.false_positive, 

618 ) 

619 extid.save() 

620 

621 return extid 

622 

623 def internal_undo(self): 

624 super().internal_undo() 

625 

626 try: 

627 extid = ExtId.objects.get( 

628 resource=self.resource, id_type=self.id_type, id_value=self.id_value 

629 ) 

630 id = extid.id 

631 extid.delete() 

632 except ExtId.DoesNotExist: 

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

634 

635 return id 

636 

637 

638class addRelatedObjectDatabaseCmd(baseCmd): 

639 """ 

640 addRelatedObjectDatabaseCmd: adds/remove a RelatedObject 

641 

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

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

644 

645 Exception raised: 

646 - ValueError if the init params are empty 

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

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

649 - RuntimeError during undo if resources are still deployed 

650 """ 

651 

652 def __init__(self, params=None): 

653 self.rel = None 

654 self.mimetype = None 

655 self.location = None 

656 self.metadata = None 

657 self.seq = None 

658 self.resource = None 

659 self.base = None 

660 

661 super().__init__(params) 

662 

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

664 

665 def set_resource(self, resource): 

666 self.resource = resource 

667 

668 def set_base(self, base): 

669 self.base = base 

670 

671 def internal_do(self): 

672 super().internal_do() 

673 

674 try: 

675 RelatedObject.objects.get( 

676 resource=self.resource, 

677 base=self.base, 

678 rel=self.rel, 

679 mimetype=self.mimetype, 

680 location=self.location, 

681 metadata=self.metadata, 

682 seq=self.seq, 

683 ) 

684 raise exceptions.ResourceExists( 

685 "The RelatedObject " 

686 + self.base 

687 + " " 

688 + self.rel 

689 + " " 

690 + self.location 

691 + " already exists" 

692 ) 

693 except RelatedObject.DoesNotExist: 

694 related_object = RelatedObject( 

695 resource=self.resource, 

696 base=self.base, 

697 rel=self.rel, 

698 mimetype=self.mimetype, 

699 location=self.location, 

700 metadata=self.metadata, 

701 seq=self.seq, 

702 ) 

703 

704 related_object.save() 

705 

706 return related_object 

707 

708 def internal_undo(self): 

709 super().internal_undo() 

710 

711 try: 

712 related_object = RelatedObject.objects.get( 

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

714 ) 

715 id = related_object.id 

716 related_object.delete() 

717 except RelatedObject.DoesNotExist: 

718 raise exceptions.ResourceDoesNotExist( 

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

720 ) 

721 

722 return id 

723 

724 

725class addSupplementaryMaterialDatabaseCmd(addRelatedObjectDatabaseCmd): 

726 """ 

727 addSupplementaryMaterialDatabaseCmd: adds/remove a Supplementary Materiel 

728 

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

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

731 

732 Exception raised: 

733 - ValueError if the init params are empty 

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

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

736 - RuntimeError during undo if resources are still deployed 

737 """ 

738 

739 def __init__(self, params=None): 

740 super().__init__(params) 

741 

742 def internal_do(self): 

743 supplementary_material, _created = SupplementaryMaterial.objects.update_or_create( 

744 caption=self.caption, 

745 resource=self.resource, 

746 base=self.base, 

747 rel=self.rel, 

748 mimetype=self.mimetype, 

749 location=self.location, 

750 metadata=self.metadata, 

751 seq=self.seq, 

752 ) 

753 return supplementary_material 

754 

755 def internal_undo(self): 

756 try: 

757 supplementary_material = SupplementaryMaterial.objects.get( 

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

759 ) 

760 pk = supplementary_material.pk 

761 supplementary_material.delete() 

762 except SupplementaryMaterial.DoesNotExist: 

763 raise exceptions.ResourceDoesNotExist( 

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

765 ) 

766 return pk 

767 

768 

769class addDataStreamDatabaseCmd(baseCmd): 

770 """ 

771 addDataStreamDatabaseCmd: adds/remove a DataStream 

772 

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

774 

775 Exception raised: 

776 - ValueError if the init params are empty 

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

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

779 - RuntimeError during undo if resources are still deployed 

780 """ 

781 

782 def __init__(self, params=None): 

783 self.rel = None 

784 self.mimetype = None 

785 self.location = None 

786 self.text = None 

787 self.seq = None 

788 self.resource = None 

789 self.base = None 

790 

791 super().__init__(params) 

792 

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

794 

795 def set_resource(self, resource): 

796 self.resource = resource 

797 

798 def set_base(self, base): 

799 self.base = base 

800 

801 def internal_do(self): 

802 super().internal_do() 

803 

804 try: 

805 DataStream.objects.get( 

806 resource=self.resource, 

807 base=self.base, 

808 rel=self.rel, 

809 mimetype=self.mimetype, 

810 location=self.location, 

811 text=self.text, 

812 seq=self.seq, 

813 ) 

814 raise exceptions.ResourceExists( 

815 "The DataStream " 

816 + self.base 

817 + " " 

818 + self.rel 

819 + " " 

820 + self.location 

821 + " already exists" 

822 ) 

823 except DataStream.DoesNotExist: 

824 datastream = DataStream( 

825 resource=self.resource, 

826 base=self.base, 

827 rel=self.rel, 

828 mimetype=self.mimetype, 

829 location=self.location, 

830 text=self.text, 

831 seq=self.seq, 

832 ) 

833 

834 datastream.save() 

835 

836 return datastream 

837 

838 def internal_undo(self): 

839 super().internal_undo() 

840 

841 try: 

842 datastream = DataStream.objects.get( 

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

844 ) 

845 id = datastream.id 

846 datastream.delete() 

847 except DataStream.DoesNotExist: 

848 raise exceptions.ResourceDoesNotExist( 

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

850 ) 

851 

852 return id 

853 

854 

855class addResourceCountDatabaseCmd(baseCmd): 

856 """ 

857 addResourceCountDatabaseCmd: adds/remove a ResourceCount 

858 

859 A ResourceCount is a generic count element. 

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

861 

862 Exception raised: 

863 - ValueError if the init params are empty 

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

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

866 - RuntimeError during undo if resources are still deployed 

867 """ 

868 

869 def __init__(self, params=None): 

870 self.seq = None 

871 self.name = None 

872 self.value = None 

873 self.resource = None 

874 self.add_total = False 

875 

876 super().__init__(params) 

877 

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

879 

880 def set_resource(self, resource): 

881 self.resource = resource 

882 

883 def internal_do(self): 

884 super().internal_do() 

885 

886 try: 

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

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

889 except ResourceCount.DoesNotExist: 

890 resource_count = ResourceCount( 

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

892 ) 

893 

894 resource_count.save() 

895 

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

897 value = parse_page_count(self.value) 

898 

899 try: 

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

901 total.value += value 

902 except Stats.DoesNotExist: 

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

904 

905 total.save() 

906 

907 return resource_count 

908 

909 def internal_undo(self): 

910 super().internal_undo() 

911 

912 try: 

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

914 id = resource_count.id 

915 resource_count.delete() 

916 except ResourceCount.DoesNotExist: 

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

918 

919 return id 

920 

921 

922class addMetaDataPartDatabaseCmd(baseCmd): 

923 """ 

924 addMetaDataPartDatabaseCmd: adds/remove a MetaDataPart 

925 

926 A MetaDataPart is a generic count element. 

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

928 

929 Exception raised: 

930 - ValueError if the init params are empty 

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

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

933 - RuntimeError during undo if resources are still deployed 

934 """ 

935 

936 def __init__(self, params=None): 

937 self.seq = None 

938 self.name = None 

939 self.data = None 

940 self.resource = None 

941 

942 super().__init__(params) 

943 

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

945 

946 def set_resource(self, resource): 

947 self.resource = resource 

948 

949 def internal_do(self): 

950 super().internal_do() 

951 

952 try: 

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

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

955 except MetaDataPart.DoesNotExist: 

956 metadatapart = MetaDataPart( 

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

958 ) 

959 

960 metadatapart.save() 

961 

962 return metadatapart 

963 

964 def internal_undo(self): 

965 super().internal_undo() 

966 

967 try: 

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

969 id = metadatapart.id 

970 metadatapart.delete() 

971 except MetaDataPart.DoesNotExist: 

972 raise exceptions.ResourceDoesNotExist( 

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

974 ) 

975 

976 return id 

977 

978 

979class addBibItemDatabaseCmd(baseCmd): 

980 """ 

981 addBibItemDatabaseCmd: adds/remove a BibItem 

982 

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

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

985 The check is actually the existence of the article. 

986 

987 Exception raised: 

988 - ValueError if the init params are empty 

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

990 - RuntimeError during undo if resources are still deployed 

991 """ 

992 

993 def __init__(self, params=None): 

994 self.sequence = None 

995 self.label = "" 

996 self.citation_xml = None 

997 self.citation_tex = None 

998 self.citation_html = None 

999 self.resource = None 

1000 

1001 self.type = "" 

1002 self.user_id = "" 

1003 self.title_tex = "" 

1004 self.publisher_name = "" 

1005 self.publisher_loc = "" 

1006 self.institution = "" 

1007 self.series = "" 

1008 self.volume = "" 

1009 self.issue = "" 

1010 self.month = "" 

1011 self.year = "" 

1012 self.comment = "" 

1013 self.annotation = "" 

1014 self.fpage = "" 

1015 self.lpage = "" 

1016 self.page_range = "" 

1017 self.size = "" 

1018 self.source = "" 

1019 self.article_title_tex = "" 

1020 self.chapter_title_tex = "" 

1021 

1022 self.contributors = None 

1023 

1024 super().__init__(params) 

1025 

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

1027 

1028 def set_resource(self, resource): 

1029 self.resource = resource 

1030 

1031 def internal_do(self): 

1032 super().internal_do() 

1033 

1034 bibitem = BibItem( 

1035 resource=self.resource, 

1036 sequence=self.sequence, 

1037 label=self.label, 

1038 citation_xml=self.citation_xml, 

1039 citation_tex=self.citation_tex, 

1040 citation_html=self.citation_html, 

1041 type=self.type, 

1042 user_id=self.user_id, 

1043 article_title_tex=self.article_title_tex, 

1044 chapter_title_tex=self.chapter_title_tex, 

1045 source_tex=self.source_tex, 

1046 publisher_name=self.publisher_name, 

1047 publisher_loc=self.publisher_loc, 

1048 institution=self.institution, 

1049 series=self.series, 

1050 volume=self.volume, 

1051 issue=self.issue, 

1052 month=self.month, 

1053 year=self.year, 

1054 comment=self.comment, 

1055 annotation=self.annotation, 

1056 fpage=self.fpage, 

1057 lpage=self.lpage, 

1058 page_range=self.page_range, 

1059 size=self.size, 

1060 ) 

1061 

1062 bibitem.save() 

1063 

1064 return bibitem 

1065 

1066 def post_do(self, bibitem): 

1067 super().post_do(bibitem) 

1068 

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

1070 

1071 def internal_undo(self): 

1072 super().internal_undo() 

1073 

1074 try: 

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

1076 id = bibitem.id 

1077 bibitem.delete() 

1078 except BibItem.DoesNotExist: 

1079 raise exceptions.ResourceDoesNotExist( 

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

1081 ) 

1082 

1083 return id 

1084 

1085 

1086class addBibItemIdDatabaseCmd(baseCmd): 

1087 """ 

1088 addBibItemIdDatabaseCmd: adds/remove a BibItemId 

1089 

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

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

1092 The check is actually the existence of the article. 

1093 

1094 Exception raised: 

1095 - ValueError if the init params are empty 

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

1097 - RuntimeError during undo if resources are still deployed 

1098 """ 

1099 

1100 def __init__(self, params=None): 

1101 self.bibitem = None 

1102 self.id_type = None 

1103 self.id_value = None 

1104 self.checked = True 

1105 self.false_positive = False 

1106 

1107 super().__init__(params) 

1108 

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

1110 

1111 def set_bibitem(self, bibitem): 

1112 self.bibitem = bibitem 

1113 

1114 def internal_do(self): 

1115 super().internal_do() 

1116 

1117 bibitemid = BibItemId( 

1118 bibitem=self.bibitem, 

1119 id_type=self.id_type, 

1120 id_value=self.id_value, 

1121 checked=self.checked, 

1122 false_positive=self.false_positive, 

1123 ) 

1124 

1125 bibitemid.save() 

1126 

1127 return bibitemid 

1128 

1129 def internal_undo(self): 

1130 super().internal_undo() 

1131 

1132 try: 

1133 bibitemid = BibItemId.objects.get( 

1134 bibitem=self.bibitem, id_type=self.id_type, id_value=self.id_value 

1135 ) 

1136 id = bibitemid.id 

1137 bibitemid.delete() 

1138 except BibItemId.DoesNotExist: 

1139 raise exceptions.ResourceDoesNotExist( 

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

1141 ) 

1142 

1143 return id 

1144 

1145 

1146class addFrontMatterDatabaseCmd(baseCmd): 

1147 """ 

1148 addFrontMatterDatabaseCmd: adds/remove a FrontMatter 

1149 

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

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

1152 The check is actually the existence of the article. 

1153 

1154 Exception raised: 

1155 - ValueError if the init params are empty 

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

1157 - RuntimeError during undo if resources are still deployed 

1158 """ 

1159 

1160 def __init__(self, params=None): 

1161 self.value_xml = "" 

1162 self.value_html = "" 

1163 self.foreword_html = "" 

1164 self.resource = None 

1165 

1166 super().__init__(params) 

1167 

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

1169 

1170 def set_resource(self, resource): 

1171 self.resource = resource 

1172 

1173 def internal_do(self): 

1174 super().internal_do() 

1175 

1176 frontmatter = FrontMatter( 

1177 resource=self.resource, 

1178 value_xml=self.value_xml, 

1179 value_html=self.value_html, 

1180 foreword_html=self.foreword_html, 

1181 ) 

1182 

1183 frontmatter.save() 

1184 

1185 return frontmatter 

1186 

1187 def internal_undo(self): 

1188 super().internal_undo() 

1189 

1190 try: 

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

1192 id = frontmatter.id 

1193 frontmatter.delete() 

1194 except FrontMatter.DoesNotExist: 

1195 raise exceptions.ResourceDoesNotExist( 

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

1197 ) 

1198 

1199 return id 

1200 

1201 

1202class addRelationshipDatabaseCmd(baseCmd): 

1203 """ 

1204 addRelationshipDatabaseCmd: adds/remove a Relationship 

1205 

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

1207 

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

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

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

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

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

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

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

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

1216 A Relationship relates 2 resources with a RelationName 

1217 

1218 

1219 Exception raised: 

1220 - ValueError if the init params are empty 

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

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

1223 - RuntimeError during undo if resources are still deployed 

1224 """ 

1225 

1226 def __init__(self, params=None): 

1227 self.subject_resource = None 

1228 self.subject_pid = None 

1229 self.object_resource = None 

1230 self.object_pid = None 

1231 self.relationname = None 

1232 

1233 super().__init__(params) 

1234 

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

1236 

1237 def set_subject_resource(self, resource): 

1238 self.subject_resource = resource 

1239 # self.subject_pid = resource.pid 

1240 

1241 def set_object_resource(self, resource): 

1242 self.object_resource = resource 

1243 # self.object_pid = resource.pid 

1244 

1245 def set_relationname(self, relationname): 

1246 self.relationname = relationname 

1247 

1248 def internal_do(self): 

1249 super().internal_do() 

1250 

1251 try: 

1252 relationship = Relationship.objects.get( 

1253 subject_pid=self.subject_pid, 

1254 object_pid=self.object_pid, 

1255 rel_info=self.relationname, 

1256 ) 

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

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

1259 # RelationShip 

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

1261 raise exceptions.ResourceExists( 

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

1263 self.subject_pid, self.relationname.left, self.object_pid 

1264 ) 

1265 ) 

1266 if self.subject_resource is not None: 

1267 relationship.resource = self.subject_resource 

1268 if self.object_resource is not None: 

1269 relationship.related = self.object_resource 

1270 relationship.save() 

1271 

1272 except Relationship.DoesNotExist: 

1273 relationship = Relationship( 

1274 subject_pid=self.subject_pid, 

1275 object_pid=self.object_pid, 

1276 resource=self.subject_resource, 

1277 related=self.object_resource, 

1278 rel_info=self.relationname, 

1279 ) 

1280 

1281 relationship.save() 

1282 

1283 return relationship 

1284 

1285 def internal_undo(self): 

1286 super().internal_undo() 

1287 

1288 try: 

1289 relationship = Relationship.objects.get( 

1290 subject_pid=self.subject_pid, 

1291 object_pid=self.object_pid, 

1292 rel_info=self.relationname, 

1293 ) 

1294 id = relationship.id 

1295 

1296 # Create relationship is typically a 2 steps process 

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

1298 # Undo is also a 2 steps process 

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

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

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

1302 # that was given as param 

1303 if self.subject_resource is None: 

1304 relationship.related = None 

1305 else: 

1306 relationship.resource = None 

1307 relationship.save() 

1308 else: 

1309 relationship.delete() 

1310 except Relationship.DoesNotExist: 

1311 raise exceptions.ResourceDoesNotExist( 

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

1313 ) 

1314 

1315 return id 

1316 

1317 

1318class addResourceDatabaseCmd(baseCmd): 

1319 """ 

1320 addResourceDatabaseCmd: base class for all resources 

1321 

1322 Exception raised: 

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

1324 """ 

1325 

1326 def __init__(self, params=None): 

1327 self.xobj = None # model_data object 

1328 

1329 self.provider = None 

1330 self._prod_deployed_date = None 

1331 

1332 super().__init__(params) 

1333 

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

1335 

1336 # May raise ValueError 

1337 def check_params(self): 

1338 super().check_params() 

1339 

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

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

1342 

1343 def set_provider(self, provider): 

1344 self.provider = provider 

1345 

1346 # May raise Provider.DoesNotExist 

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

1348 self.provider = Provider.objects.get( 

1349 name=provider_name, pid_type=pid_type, sid_type=sid_type 

1350 ) 

1351 

1352 def post_do(self, resource): 

1353 super().post_do(resource) 

1354 self.object_to_be_deleted = resource 

1355 

1356 site = model_helpers.get_or_create_site(settings.SITE_ID, resource.pid) 

1357 if site: 1357 ↛ 1363line 1357 didn't jump to line 1363 because the condition on line 1357 was always true

1358 resource.deploy(site, self._prod_deployed_date) 

1359 

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

1361 # Restoration of SiteMembership is handled in importExtraDataPtfCmd 

1362 

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

1364 if ( 1364 ↛ 1363line 1364 didn't jump to line 1363

1365 id_type != self.provider.pid_type 

1366 and id_type != self.provider.sid_type 

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

1368 ): 

1369 try: 

1370 # May raise Resource.DoesNotExist 

1371 resource_id = ResourceId.objects.get( 

1372 resource=resource, id_type=id_type, id_value=id_value 

1373 ) 

1374 except ResourceId.DoesNotExist: 

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

1376 resource_id.save() 

1377 

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

1379 if ( 1379 ↛ 1378line 1379 didn't jump to line 1378

1380 id_type != self.provider.pid_type 

1381 and id_type != self.provider.sid_type 

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

1383 ): 

1384 try: 

1385 # May raise Resource.DoesNotExist 

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

1387 except ExtId.DoesNotExist: 

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

1389 ext_id.save() 

1390 

1391 seq = 1 

1392 # abstract_set = self.xobj.abstract_set.all() 

1393 for a in self.xobj.abstracts: 

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

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

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

1397 

1398 la = Abstract( 

1399 resource=resource, 

1400 tag=a["tag"], 

1401 lang=a["lang"], 

1402 seq=seq, 

1403 value_xml=value_xml, 

1404 value_html=value_html, 

1405 value_tex=value_tex, 

1406 ) 

1407 la.save() 

1408 seq += 1 

1409 

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

1411 

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

1413 k = Kwd( 

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

1415 ) 

1416 k.save() 

1417 

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

1419 s = Subj( 

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

1421 ) 

1422 s.save() 

1423 

1424 seq = 1 

1425 for a in self.xobj.awards: 1425 ↛ 1426line 1425 didn't jump to line 1426 because the loop on line 1425 never started

1426 abbrev = a["abbrev"] 

1427 award_id = a["award_id"] 

1428 

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

1430 award.save() 

1431 seq += 1 

1432 

1433 # TODO custom meta 

1434 

1435 def pre_undo(self): 

1436 super().pre_undo() 

1437 

1438 # None value not detected in check_params (required_delete_params) 

1439 # => undo was already called 

1440 if self.object_to_be_deleted is None: 

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

1442 

1443 # Django automatically deletes related objects such as ResourceIds, 

1444 # Abstracts... 

1445 

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

1447 # Another solution would be to do it here 

1448 

1449 # Undeploy the resource in all sites 

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

1451 self.object_to_be_deleted.undeploy(site) 

1452 

1453 def internal_undo(self): 

1454 super().internal_undo() 

1455 

1456 id = self.object_to_be_deleted.id 

1457 self.object_to_be_deleted.delete() 

1458 

1459 return id 

1460 

1461 def post_undo(self): 

1462 super().internal_undo() 

1463 

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

1465 

1466 

1467class addPublisherDatabaseCmd(baseCmd): 

1468 """ 

1469 addPublisherDatabaseCmd: adds/remove a publisher 

1470 

1471 Exception raised: 

1472 - ValueError if the init params are empty 

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

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

1475 """ 

1476 

1477 def __init__(self, params=None): 

1478 self.xobj = None # model_data object 

1479 self.object_to_be_deleted = None 

1480 

1481 super().__init__(params) 

1482 

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

1484 self.required_delete_params.append("object_to_be_deleted") 

1485 

1486 def set_object_to_be_deleted(self, obj): 

1487 self.object_to_be_deleted = obj 

1488 

1489 def internal_do(self): 

1490 super().internal_do() 

1491 

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

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

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

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

1496 

1497 try: 

1498 Publisher.objects.get(pub_key=key) 

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

1500 except Publisher.DoesNotExist: 

1501 publisher = Publisher( 

1502 pub_key=key, 

1503 pub_name=self.xobj.name, 

1504 pub_loc=self.xobj.loc, 

1505 pid=key, 

1506 provider=provider, 

1507 ) 

1508 publisher.save() 

1509 

1510 self.object_to_be_deleted = publisher 

1511 return publisher 

1512 

1513 def internal_undo(self): 

1514 super().internal_undo() 

1515 

1516 # None value not detected in check_params (required_delete_params) 

1517 # => undo was already called 

1518 if self.object_to_be_deleted is None: 

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

1520 

1521 self.object_to_be_deleted.refresh_from_db() 

1522 

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

1524 raise RuntimeError( 

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

1526 ) 

1527 

1528 id = self.object_to_be_deleted.id 

1529 self.object_to_be_deleted.delete() 

1530 self.object_to_be_deleted = None 

1531 

1532 return id 

1533 

1534 

1535class addContainerDatabaseCmd(addResourceDatabaseCmd): 

1536 """ 

1537 addContainerDatabaseCmd: adds/remove a container 

1538 

1539 an Container needs a Collection 

1540 

1541 Exception raised: 

1542 - ValueError if the init params are empty 

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

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

1545 """ 

1546 

1547 def __init__(self, params=None): 

1548 self.last_modified = None 

1549 self.collection = None 

1550 self._publisher = None 

1551 

1552 super().__init__(params) 

1553 

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

1555 

1556 def add_collection(self, collection): 

1557 if not self.collection: 1557 ↛ exitline 1557 didn't return from function 'add_collection' because the condition on line 1557 was always true

1558 self.collection = collection 

1559 

1560 def pre_do(self): 

1561 super().pre_do() 

1562 

1563 self._publisher = add_publisher(self.xobj) 

1564 

1565 def internal_do(self): 

1566 super().internal_do() 

1567 

1568 vseries = volume = number = seq = "" 

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

1570 vseries = self.xobj.vseries 

1571 volume = self.xobj.volume 

1572 number = self.xobj.number 

1573 seq = self.xobj.seq 

1574 elif len(self.xobj.incollection) > 0: 1574 ↛ 1583line 1574 didn't jump to line 1583 because the condition on line 1574 was always true

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

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

1577 incol = self.xobj.incollection[0] 

1578 vseries = incol.vseries 

1579 volume = "" 

1580 number = incol.volume 

1581 seq = incol.seq 

1582 

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

1584 

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

1586 if self.xobj.prod_deployed_date_iso_8601_date_str: 

1587 self._prod_deployed_date = model_helpers.parse_date_str( 

1588 self.xobj.prod_deployed_date_iso_8601_date_str 

1589 ) 

1590 

1591 with_online_first = ( 

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

1593 ) 

1594 

1595 try: 

1596 Container.objects.get( 

1597 pid=self.xobj.pid, 

1598 provider__pid_type=self.provider.pid_type, 

1599 my_collection__pid=self.collection.pid, 

1600 ) 

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

1602 

1603 except Container.DoesNotExist: 

1604 container = Container( 

1605 ctype=self.xobj.ctype, 

1606 doi=self.xobj.doi, 

1607 pid=self.xobj.pid, 

1608 lang=self.xobj.lang, 

1609 title_xml=self.xobj.title_xml, 

1610 title_tex=self.xobj.title_tex, 

1611 title_html=self.xobj.title_html, 

1612 trans_lang=self.xobj.trans_lang, 

1613 trans_title_tex=self.xobj.trans_title_tex, 

1614 trans_title_html=self.xobj.trans_title_html, 

1615 provider=self.provider, 

1616 my_publisher=self._publisher, 

1617 my_collection=self.collection, 

1618 year=self.xobj.year, 

1619 vseries=vseries, 

1620 vseries_int=make_int(vseries), 

1621 volume=volume, 

1622 volume_int=make_int(volume), 

1623 number=number, 

1624 number_int=make_int(number), 

1625 seq=seq, 

1626 last_modified=last_modified, 

1627 with_online_first=with_online_first, 

1628 body_html=self.xobj.body_html, 

1629 body_tex=self.xobj.body_tex, 

1630 body_xml=self.xobj.body_xml, 

1631 ) 

1632 

1633 container.save() 

1634 

1635 return container 

1636 

1637 def post_do(self, container): 

1638 super().post_do(container) 

1639 

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

1641 for incol in self.xobj.incollection: 

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

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

1644 collection = model_helpers.get_collection(incol.pid) 

1645 if collection: 1645 ↛ 1641line 1645 didn't jump to line 1641 because the condition on line 1645 was always true

1646 collection_membership = CollectionMembership( 

1647 collection=collection, 

1648 container=container, 

1649 seq=incol.seq, 

1650 vseries=incol.vseries, 

1651 volume="", 

1652 number=incol.volume, 

1653 vseries_int=make_int(incol.vseries), 

1654 volume_int=0, 

1655 number_int=make_int(incol.volume), 

1656 ) 

1657 collection_membership.save() 

1658 

1659 add_biblio(self.xobj, container) 

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

1661 add_relations(self.xobj, container) 

1662 add_frontmatter(self.xobj, container) 

1663 

1664 def post_undo(self): 

1665 collection = self.object_to_be_deleted.get_collection() 

1666 super().post_undo() 

1667 

1668 if self._publisher is not None: 

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

1670 self._publisher.delete() 

1671 self._publisher = None 

1672 

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

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

1675 # if it no longer has any content 

1676 collection.delete() 

1677 

1678 

1679class addResourceInSpecialIssueDatabaseCmd(baseCmd): 

1680 """ 

1681 addResourceInSpecialIssueDatabaseCmd: adds/remove a ResourceInSpecialIssue 

1682 

1683 a ResourceInSpecialIssue needs a issue 

1684 

1685 params : 

1686 - obj : the resource 'article, book, ...' 

1687 - obj_doi: the doi of the resource 

1688 - container : the special issue using the resource 

1689 - seq : sequence of the resource in the special issue 

1690 - citation : html citation of the resource 

1691 

1692 Exception raised: 

1693 - ValueError if the init params are empty 

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

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

1696 """ 

1697 

1698 def __init__(self, params=None): 

1699 # parents are used for book parts. 

1700 

1701 # Container containing the article 

1702 self.container = params["container"] 

1703 # self.collection = None 

1704 # self.provider = params["provider"] 

1705 self.obj_doi = params["obj_doi"] 

1706 self.seq = params["seq"] 

1707 # self.xobj = params["xobj"] 

1708 self.citation = params["citation"] 

1709 # self.resource = None 

1710 

1711 # super().__init__(params) 

1712 

1713 def do(self): 

1714 # super().internal_do() 

1715 # changer nom resource en x... 

1716 # xresource = self.xobj 

1717 resource_doi = self.obj_doi 

1718 container = self.container 

1719 seq = self.seq 

1720 citation = self.citation 

1721 # if not citation: 

1722 # citation = resource.citation_html 

1723 # juste mettre la vérif sur le champ doi car toutes les resources ont ce champ 

1724 # et mettre model_helper get_resource... 

1725 # if isinstance(resource, jats_parser.JatsArticle): 

1726 if resource_doi: 

1727 resource = Article.objects.get(doi=resource_doi) 

1728 else: 

1729 pass 

1730 try: 

1731 ResourceInSpecialIssue.objects.get( 

1732 resource=resource, resource_doi=resource_doi, my_container=container 

1733 ) 

1734 raise exceptions.ResourceExists( 

1735 f"The ResourceInSpecialIssue {resource} for issue {container} already exists" 

1736 ) 

1737 

1738 except ResourceInSpecialIssue.DoesNotExist: 

1739 resource_in_special_issue = ResourceInSpecialIssue( 

1740 resource=resource, 

1741 resource_doi=resource_doi, 

1742 my_container=container, 

1743 citation=citation, 

1744 seq=seq, 

1745 ) 

1746 

1747 resource_in_special_issue.save() 

1748 return resource_in_special_issue 

1749 

1750 

1751class addArticleDatabaseCmd(addResourceDatabaseCmd): 

1752 """ 

1753 addArticleDatabaseCmd: adds/remove an article 

1754 

1755 an article needs a container (book or issue) 

1756 

1757 Exception raised: 

1758 - ValueError if the init params are empty 

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

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

1761 """ 

1762 

1763 def __init__(self, params=None): 

1764 # parents are used for book parts. 

1765 self.pseq = 0 # parent seq 

1766 self.parent = None # article parent 

1767 

1768 # Container containing the article 

1769 self.container = None 

1770 self.collection = None 

1771 

1772 self.assign_doi = False 

1773 self.seq = 0 

1774 

1775 self.translated_articles = [] 

1776 

1777 super().__init__(params) 

1778 

1779 def check_params(self): 

1780 super().check_params() 

1781 

1782 check_title = True 

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

1784 check_title = False 

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

1786 # A Translation may not have a title 

1787 check_title = False 

1788 if ( 1788 ↛ 1794line 1788 didn't jump to line 1794

1789 check_title 

1790 and self.is_add_cmd 

1791 and not self.xobj.title_tex 

1792 and not self.xobj.trans_title_tex 

1793 ): 

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

1795 

1796 def set_container(self, container): 

1797 self.container = container 

1798 self.collection = container.my_collection 

1799 self.set_provider(container.provider) 

1800 

1801 def set_collection(self, collection): 

1802 self.collection = collection 

1803 self.set_provider(collection.provider) 

1804 

1805 def set_parent(self, parent): 

1806 self.parent = parent 

1807 

1808 def parse_dates(self): 

1809 dates = { 

1810 "accepted": None, 

1811 "received": None, 

1812 "revised": None, 

1813 "online": None, 

1814 "published": None, 

1815 } 

1816 

1817 for date_entry in self.xobj.history_dates: 

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

1819 raise ValueError( 

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

1821 ) 

1822 

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

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

1825 

1826 if self.xobj.date_published_iso_8601_date_str: 

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

1828 self.xobj.date_published_iso_8601_date_str 

1829 ) 

1830 

1831 if self.xobj.prod_deployed_date_iso_8601_date_str: 

1832 self._prod_deployed_date = model_helpers.parse_date_str( 

1833 self.xobj.prod_deployed_date_iso_8601_date_str 

1834 ) 

1835 

1836 return dates 

1837 

1838 def add_translations(self, xobj, article): 

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

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

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

1842 cmd.set_original_article(article) 

1843 cmd.set_provider(article.provider) 

1844 trans_article = cmd.do() 

1845 self.translated_articles.append(trans_article) 

1846 

1847 def internal_do(self): 

1848 super().internal_do() 

1849 

1850 doi = self.xobj.doi 

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

1852 colid = self.container.my_collection.pid 

1853 doi = model_helpers.assign_doi(colid) 

1854 

1855 seq = self.xobj.seq or self.seq 

1856 dates = self.parse_dates() 

1857 

1858 try: 

1859 Article.objects.get( 

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

1861 ) 

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

1863 except Article.DoesNotExist: 

1864 article = Article( 

1865 pid=self.xobj.pid, 

1866 doi=doi, 

1867 lang=self.xobj.lang, 

1868 title_xml=self.xobj.title_xml, 

1869 title_tex=self.xobj.title_tex, 

1870 title_html=self.xobj.title_html, 

1871 trans_lang=self.xobj.trans_lang, 

1872 trans_title_tex=self.xobj.trans_title_tex, 

1873 trans_title_html=self.xobj.trans_title_html, 

1874 provider=self.provider, 

1875 my_container=self.container, 

1876 fpage=self.xobj.fpage, 

1877 lpage=self.xobj.lpage, 

1878 seq=seq, 

1879 atype=self.xobj.atype, 

1880 page_range=self.xobj.page_range, 

1881 page_type=self.xobj.page_type, 

1882 elocation=self.xobj.elocation, 

1883 article_number=self.xobj.article_number, 

1884 talk_number=self.xobj.talk_number, 

1885 pseq=self.pseq, 

1886 parent=self.parent, 

1887 date_accepted=dates["accepted"], 

1888 date_received=dates["received"], 

1889 date_revised=dates["revised"], 

1890 date_online_first=dates["online"], 

1891 date_published=dates["published"], 

1892 coi_statement=self.xobj.coi_statement, 

1893 funding_statement_html=self.xobj.funding_statement_html, 

1894 funding_statement_xml=self.xobj.funding_statement_xml, 

1895 footnotes_html=self.xobj.footnotes_html, 

1896 footnotes_xml=self.xobj.footnotes_xml, 

1897 body_html=self.xobj.body_html, 

1898 body_tex=self.xobj.body_tex, 

1899 body_xml=self.xobj.body_xml, 

1900 ) 

1901 

1902 article.save() 

1903 

1904 if doi: 

1905 collection = self.collection 

1906 doi_number = model_helpers.get_number_from_doi(doi) 

1907 if doi_number > collection.last_doi: 

1908 collection.last_doi = doi_number 

1909 collection.save() 

1910 

1911 return article 

1912 

1913 def post_do(self, article): 

1914 super().post_do(article) 

1915 

1916 add_biblio(self.xobj, article) 

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

1918 add_relations(self.xobj, article) 

1919 self.add_translations(self.xobj, article) 

1920 

1921 

1922class addCollectionDatabaseCmd(addResourceDatabaseCmd): 

1923 """ 

1924 addCollectionDatabaseCmd: adds/remove a collection 

1925 

1926 Exception raised: 

1927 - ValueError if the init params are empty 

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

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

1930 """ 

1931 

1932 def __init__(self, params=None): 

1933 # self.coltype = None 

1934 # self.wall = 5 

1935 self.parent = None 

1936 

1937 super().__init__(params) 

1938 

1939 def set_parent(self, parent): 

1940 self.parent = parent 

1941 

1942 def internal_do(self): 

1943 super().internal_do() 

1944 

1945 title_sort = ( 

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

1947 ) 

1948 

1949 try: 

1950 col = Collection.objects.get( 

1951 pid=self.xobj.pid, 

1952 title_tex=self.xobj.title_tex, 

1953 provider__pid_type=self.provider.pid_type, 

1954 ) 

1955 except Collection.DoesNotExist: 

1956 col = Collection.objects.create( 

1957 coltype=self.xobj.coltype, 

1958 abbrev=self.xobj.abbrev, 

1959 wall=self.xobj.wall, 

1960 pid=self.xobj.pid, 

1961 lang=self.xobj.lang, 

1962 title_xml=self.xobj.title_xml, 

1963 title_tex=self.xobj.title_tex, 

1964 title_html=self.xobj.title_html, 

1965 title_sort=title_sort, 

1966 trans_lang=self.xobj.trans_lang, 

1967 trans_title_tex=self.xobj.trans_title_tex, 

1968 trans_title_html=self.xobj.trans_title_html, 

1969 provider=self.provider, 

1970 parent=self.parent, 

1971 ) 

1972 else: 

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

1974 return col 

1975 

1976 # def internal_undo(self): 

1977 # super().internal_undo() 

1978 # 

1979 # try: 

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

1981 # provider__pid_type=self.provider.pid_type) 

1982 # id = col.id 

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

1984 # col.parent = None 

1985 # col.delete() 

1986 # 

1987 # 

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

1989 # col.delete() 

1990 # 

1991 # except Collection.DoesNotExist: 

1992 # raise exceptions.ResourceDoesNotExist( 

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

1994 # 

1995 # return id 

1996 

1997 

1998class addTranslatedArticleDatabaseCmd(addArticleDatabaseCmd): 

1999 """ 

2000 addTranslatedArticleDatabaseCmd: adds/remove a translated article 

2001 """ 

2002 

2003 def __init__(self, params=None): 

2004 super().__init__(params) 

2005 

2006 self.original_article = None 

2007 

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

2009 

2010 def set_original_article(self, original_article): 

2011 self.original_article = original_article 

2012 

2013 def internal_do(self): 

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

2015 

2016 dates = self.parse_dates() 

2017 

2018 try: 

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

2020 raise exceptions.ResourceExists( 

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

2022 ) 

2023 except Article.DoesNotExist: 

2024 article = TranslatedArticle( 

2025 original_article=self.original_article, 

2026 pid=self.xobj.pid, 

2027 doi=self.xobj.doi, 

2028 lang=self.xobj.lang, 

2029 title_xml=self.xobj.title_xml, 

2030 title_tex=self.xobj.title_tex, 

2031 title_html=self.xobj.title_html, 

2032 trans_lang=self.xobj.trans_lang, 

2033 trans_title_tex=self.xobj.trans_title_tex, 

2034 trans_title_html=self.xobj.trans_title_html, 

2035 provider=self.provider, 

2036 my_container=None, 

2037 fpage=self.xobj.fpage, 

2038 lpage=self.xobj.lpage, 

2039 seq=self.xobj.seq, 

2040 atype=self.xobj.atype, 

2041 page_range=self.xobj.page_range, 

2042 page_type=self.xobj.page_type, 

2043 elocation=self.xobj.elocation, 

2044 article_number=self.xobj.article_number, 

2045 talk_number=self.xobj.talk_number, 

2046 pseq=self.pseq, 

2047 parent=self.parent, 

2048 date_accepted=dates["accepted"], 

2049 date_received=dates["received"], 

2050 date_revised=dates["revised"], 

2051 date_online_first=dates["online"], 

2052 date_published=dates["published"], 

2053 coi_statement=self.xobj.coi_statement, 

2054 funding_statement_html=self.xobj.funding_statement_html, 

2055 funding_statement_xml=self.xobj.funding_statement_xml, 

2056 footnotes_html=self.xobj.footnotes_html, 

2057 footnotes_xml=self.xobj.footnotes_xml, 

2058 body_html=self.xobj.body_html, 

2059 body_tex=self.xobj.body_tex, 

2060 body_xml=self.xobj.body_xml, 

2061 ) 

2062 

2063 article.save() 

2064 

2065 return article 

2066 

2067 def post_do(self, container): 

2068 super().post_do(container) 

2069 

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

2071 for incol in self.xobj.incollection: 

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

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

2074 collection = model_helpers.get_collection(incol.pid) 

2075 if collection: 

2076 collection_membership = CollectionMembership( 

2077 collection=collection, 

2078 container=container, 

2079 seq=incol.seq, 

2080 vseries=incol.vseries, 

2081 volume="", 

2082 number=incol.volume, 

2083 vseries_int=make_int(incol.vseries), 

2084 volume_int=0, 

2085 number_int=make_int(incol.volume), 

2086 ) 

2087 collection_membership.save() 

2088 

2089 add_biblio(self.xobj, container) 

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

2091 add_relations(self.xobj, container) 

2092 add_frontmatter(self.xobj, container) 

2093 

2094 def post_undo(self): 

2095 collection = self.object_to_be_deleted.get_collection() 

2096 super().post_undo() 

2097 

2098 if self._publisher is not None: 

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

2100 self._publisher.delete() 

2101 self._publisher = None 

2102 

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

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

2105 # if it no longer has any content 

2106 collection.delete() 

2107 

2108 

2109########################################################################## 

2110########################################################################## 

2111# 

2112# Update Commands 

2113# 

2114########################################################################## 

2115########################################################################## 

2116 

2117 

2118class updateCollectionDatabaseCmd(addResourceDatabaseCmd): 

2119 """ 

2120 updateCollectionDatabaseCmd: updates a Collection (journal, acta) 

2121 

2122 a Collection needs a Provider 

2123 

2124 Exception raised: 

2125 - ValueError if the init params are empty 

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

2127 """ 

2128 

2129 def __init__(self, params=None): 

2130 super().__init__(params) 

2131 

2132 def internal_do(self): 

2133 super().internal_do() 

2134 

2135 try: 

2136 collection = Collection.objects.get( 

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

2138 ) 

2139 except Collection.DoesNotExist: 

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

2141 

2142 # delete objects in direct relation with the collection 

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

2144 # addResourceDatabaseCmd::post_do 

2145 collection.extlink_set.all().delete() 

2146 collection.resourceid_set.all().delete() 

2147 collection.abstract_set.all().delete() 

2148 

2149 title_sort = ( 

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

2151 ) 

2152 

2153 collection.coltype = self.xobj.coltype 

2154 collection.lang = self.xobj.lang 

2155 collection.title_xml = self.xobj.title_xml 

2156 collection.title_tex = self.xobj.title_tex 

2157 collection.title_html = self.xobj.title_html 

2158 collection.title_sort = title_sort 

2159 collection.abbrev = self.xobj.abbrev 

2160 

2161 collection.wall = self.xobj.wall 

2162 collection.save() 

2163 

2164 return collection 

2165 

2166 

2167class updateExtLinkDatabaseCmd(baseCmd): 

2168 """ 

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

2170 updateExtLinkDatabaseCmd: 

2171 - updates an ExtLink, or 

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

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

2174 

2175 Exception raised: 

2176 - ValueError if the init params are empty 

2177 """ 

2178 

2179 def __init__(self, params=None): 

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

2181 self.mimetype = None 

2182 self.location = None 

2183 self.metadata = None 

2184 self.seq = None 

2185 self.resource = None 

2186 self.base = None 

2187 

2188 super().__init__(params) 

2189 

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

2191 

2192 def set_resource(self, resource): 

2193 self.resource = resource 

2194 

2195 def set_base(self, base): 

2196 self.base = base 

2197 

2198 def internal_do(self): 

2199 super().internal_do() 

2200 

2201 extlink = None 

2202 

2203 try: 

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

2205 self.seq = extlink.seq 

2206 

2207 except ExtLink.DoesNotExist: 

2208 if self.location: 

2209 if not self.seq: 

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

2211 

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

2213 extlink.save() 

2214 

2215 if self.location: 

2216 extlink.location = self.location 

2217 

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

2219 self.metadata = "website" 

2220 

2221 if self.base: 

2222 extlink.base = self.base 

2223 if self.mimetype: 

2224 extlink.mimetype = self.mimetype 

2225 if self.metadata: 

2226 extlink.metadata = self.metadata 

2227 

2228 extlink.seq = self.seq 

2229 

2230 extlink.save() 

2231 elif extlink: 

2232 extlink.delete() 

2233 extlink = None 

2234 

2235 return extlink 

2236 

2237 

2238class updateResourceIdDatabaseCmd(baseCmd): 

2239 """ 

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

2241 updateResourceIdDatabaseCmd: 

2242 - updates an ResourceId, or 

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

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

2245 

2246 Exception raised: 

2247 - ValueError if the init params are empty 

2248 """ 

2249 

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

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

2252 self.id_value = None 

2253 self.resource = None 

2254 

2255 super().__init__(params) 

2256 

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

2258 

2259 def set_resource(self, resource): 

2260 self.resource = resource 

2261 

2262 def internal_do(self): 

2263 super().internal_do() 

2264 

2265 resourceid = None 

2266 id_type = self.id_type 

2267 id_value = self.id_value 

2268 

2269 if id_type == "ojs-id": 

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

2271 # create an internal id 

2272 if id_value == "edito": 

2273 qs = ResourceId.objects.filter( 

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

2275 ) 

2276 count = qs.count() 

2277 if count: 

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

2279 else: 

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

2281 count = qs.count() 

2282 if count: 

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

2284 try: 

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

2286 

2287 except ResourceId.DoesNotExist: 

2288 if id_value: 

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

2290 

2291 if id_value: 

2292 resourceid.id_value = id_value 

2293 resourceid.save() 

2294 elif resourceid: 

2295 resourceid.delete() 

2296 resourceid = None 

2297 

2298 return resourceid 

2299 

2300 

2301class deleteResourceDatabaseCmd(baseCmd): 

2302 """ 

2303 deleteResourceDatabaseCmd: base class for all resources 

2304 NOT USED. TODO: remove 

2305 """ 

2306 

2307 def __init__(self, params=None): 

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

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

2310 self.provider = None 

2311 

2312 super().__init__(params) 

2313 

2314 def pre_do(self): 

2315 super().pre_do() 

2316 

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

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

2319 

2320 if self.provider is None: 

2321 raise ValueError("provider est vide") 

2322 

2323 def set_provider(self, provider): 

2324 self.provider = provider 

2325 

2326 # May raise Provider.DoesNotExist 

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

2328 self.provider = Provider.objects.get( 

2329 name=provider_name, pid_type=pid_type, sid_type=sid_type 

2330 ) 

2331 

2332 

2333class publishArticleDatabaseCmd(baseCmd): 

2334 """ 

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

2336 @return: list of updated articles 

2337 """ 

2338 

2339 def __init__(self, params=None): 

2340 self.article = None 

2341 self.container = None 

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

2343 self.update_db = True 

2344 super().__init__(params) 

2345 

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

2347 

2348 def set_article(self, article): 

2349 self.article = article 

2350 self.container = article.my_container 

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

2352 self.container = self.article.original_article.my_container 

2353 

2354 def publish_article(self, article, update_articles): 

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

2356 # the publication order. 

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

2358 update_article_number = False 

2359 # TODO: use a param instead of checking PCJ 

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

2361 update_article_number = True 

2362 

2363 article_number = ( 

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

2365 ) 

2366 

2367 if (not article.date_published and not self.pre_publish) or ( 2367 ↛ exitline 2367 didn't return from function 'publish_article' because the condition on line 2367 was always true

2368 not article.date_pre_published and self.pre_publish 

2369 ): 

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

2371 

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

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

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

2375 year = article.get_year() 

2376 fyear, lyear = model_helpers.get_first_last_years(year) 

2377 try: 

2378 fyear = int(fyear) 

2379 except ValueError: 

2380 fyear = 0 

2381 

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

2383 if self.pre_publish: 2383 ↛ 2384line 2383 didn't jump to line 2384 because the condition on line 2383 was never true

2384 article.date_pre_published = today 

2385 else: 

2386 article.date_published = today 

2387 if update_article_number: 2387 ↛ 2388line 2387 didn't jump to line 2388 because the condition on line 2387 was never true

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

2389 article_number += 1 

2390 if self.update_db: 2390 ↛ 2392line 2390 didn't jump to line 2392 because the condition on line 2390 was always true

2391 article.save() 

2392 update_articles.append(article) 

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

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

2395 # Online First 

2396 article.date_online_first = today 

2397 if self.update_db: 

2398 article.save() 

2399 update_articles.append(article) 

2400 elif self.pre_publish: 

2401 article.date_pre_published = today 

2402 if self.update_db: 

2403 article.save() 

2404 update_articles.append(article) 

2405 

2406 def internal_do(self): 

2407 update_articles = [] 

2408 

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

2410 # the publication order. 

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

2412 update_article_number = False 

2413 # TODO: use a param instead of checking PCJ 

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

2415 update_article_number = True 

2416 

2417 self.publish_article(self.article, update_articles) 

2418 

2419 # TODO: update in upload articles ? 

2420 

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

2422 if update_article_number: 

2423 i = 1 

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

2425 "-date_published" 

2426 ): 

2427 article.seq = i 

2428 i += 1 

2429 article.save() 

2430 

2431 return update_articles 

2432 

2433 

2434class publishContainerDatabaseCmd(publishArticleDatabaseCmd): 

2435 """ 

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

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

2438 @return: list of updated articles 

2439 """ 

2440 

2441 def __init__(self, params=None): 

2442 super().__init__(params) 

2443 

2444 self.required_params = ["container"] 

2445 

2446 def set_container(self, container): 

2447 self.container = container 

2448 

2449 def internal_do(self): 

2450 update_articles = [] 

2451 

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

2453 # the publication order. 

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

2455 update_article_number = False 

2456 # TODO: use a param instead of checking PCJ 

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

2458 update_article_number = True 

2459 

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

2461 self.publish_article(article, update_articles) 

2462 

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

2464 if update_article_number: 2464 ↛ 2465line 2464 didn't jump to line 2465 because the condition on line 2464 was never true

2465 i = 1 

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

2467 "-date_published" 

2468 ): 

2469 article.seq = i 

2470 i += 1 

2471 article.save() 

2472 

2473 return update_articles 

2474 

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

2476 

2477 

2478def convert_contribs(pid, delete=False): 

2479 if pid == "PCJ": 

2480 # PCJ: pid start with "10_24072" 

2481 pid = "10_24072" 

2482 

2483 if delete: 

2484 revert_contribs(pid) 

2485 

2486 if pid is not None: 

2487 qs = Contrib.objects.filter( 

2488 Q(group__resource__pid__startswith=pid) 

2489 | Q(group__bibitem__resource__pid__startswith=pid) 

2490 ) 

2491 else: 

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

2493 

2494 total = qs.count() 

2495 i = 1 

2496 percent = 0 

2497 

2498 for contrib in qs: 

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

2500 percent += 1 

2501 if percent % 5 == 0: 

2502 print(pid, percent) 

2503 i += 1 

2504 

2505 # contrib_fields_list = Contrib.get_fields_list() 

2506 # contrib_fields = { 

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

2508 # } 

2509 

2510 # Augment the contrib attributes to match Contribution fields 

2511 role = contrib.contrib_type 

2512 if not role: 

2513 role = contrib.group.content_type 

2514 if not role: 

2515 role = "author" 

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

2517 role = role[0:-1] 

2518 contrib.corresponding = role == "corresponding" 

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

2520 

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

2522 if ref_name is None: 

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

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

2525 contrib.first_letter = letter 

2526 

2527 contrib.deceased_before_publication = contrib.deceased 

2528 contrib.idref = None 

2529 contrib.mid = None 

2530 

2531 contribution_fields_list = Contribution.get_fields_list() 

2532 contributions_fields = { 

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

2534 } 

2535 

2536 contribution = Contribution( 

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

2538 ) 

2539 contribution.save() 

2540 

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

2542 contrib_address.contribution = contribution 

2543 contrib_address.save() 

2544 

2545 qs = Contribution.objects.filter( 

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

2547 ) 

2548 print(qs.count()) 

2549 

2550 

2551def revert_contribs(pid): 

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

2553 Contribution.objects.filter( 

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

2555 ).delete() 

2556 print("done")