add comic artists
This commit is contained in:
Binary file not shown.
|
After Width: | Height: | Size: 544 B |
Binary file not shown.
|
After Width: | Height: | Size: 17 KiB |
Binary file not shown.
|
After Width: | Height: | Size: 634 B |
@@ -1,5 +1,4 @@
|
|||||||
import { Component, DestroyRef, inject, OnInit, signal } from '@angular/core';
|
import { Component, DestroyRef, inject, OnInit, signal } from '@angular/core';
|
||||||
import { RouterLink, RouterLinkActive } from '@angular/router';
|
|
||||||
import { Artist } from '../comic-artists/artist.model';
|
import { Artist } from '../comic-artists/artist.model';
|
||||||
import { ComicArtistComponent } from '../comic-artist/comic-artist.component';
|
import { ComicArtistComponent } from '../comic-artist/comic-artist.component';
|
||||||
import { ArtistService } from '../comic-artists/artist.service';
|
import { ArtistService } from '../comic-artists/artist.service';
|
||||||
|
|||||||
@@ -2,3 +2,9 @@ export interface Artist {
|
|||||||
id: string;
|
id: string;
|
||||||
name: string;
|
name: string;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export interface ArtistDetails {
|
||||||
|
id: string;
|
||||||
|
name: string;
|
||||||
|
weblink: string;
|
||||||
|
}
|
||||||
@@ -2,7 +2,7 @@ import { inject, Injectable, signal } from "@angular/core";
|
|||||||
import { HttpClient } from "@angular/common/http";
|
import { HttpClient } from "@angular/common/http";
|
||||||
import { catchError, map, throwError } from "rxjs";
|
import { catchError, map, throwError } from "rxjs";
|
||||||
import { ErrorService } from "../../../shared/error.service";
|
import { ErrorService } from "../../../shared/error.service";
|
||||||
import { Artist } from "./artist.model";
|
import { Artist, ArtistDetails } from "./artist.model";
|
||||||
|
|
||||||
@Injectable({
|
@Injectable({
|
||||||
providedIn: 'root',
|
providedIn: 'root',
|
||||||
@@ -18,6 +18,10 @@ export class ArtistService {
|
|||||||
return this.fetchArtists('http://127.0.0.1:8800/api/comics/artists', 'Someting went wrong fetching artists. Please try again later-');
|
return this.fetchArtists('http://127.0.0.1:8800/api/comics/artists', 'Someting went wrong fetching artists. Please try again later-');
|
||||||
}
|
}
|
||||||
|
|
||||||
|
loadArtistDetails(artistId: string | null) {
|
||||||
|
return this.fetchArtistDetails('http://127.0.0.1:8800/api/comics/artists/' + artistId, 'Someting went wrong fetching comic artists. Please try again later.');
|
||||||
|
}
|
||||||
|
|
||||||
private fetchArtists(url: string, errorMessage: string) {
|
private fetchArtists(url: string, errorMessage: string) {
|
||||||
return this.httpClient.get<Artist[]>(url).pipe(
|
return this.httpClient.get<Artist[]>(url).pipe(
|
||||||
map((resData) => resData),
|
map((resData) => resData),
|
||||||
@@ -27,4 +31,17 @@ export class ArtistService {
|
|||||||
})
|
})
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private fetchArtistDetails(url: string, errorMessage: string) {
|
||||||
|
return this.httpClient.get<ArtistDetails>(url).pipe(
|
||||||
|
map((resData) => {
|
||||||
|
console.log(resData);
|
||||||
|
return resData;
|
||||||
|
}),
|
||||||
|
catchError((error) => {
|
||||||
|
console.log(error);
|
||||||
|
return throwError(() => new Error(errorMessage));
|
||||||
|
})
|
||||||
|
);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -4,6 +4,11 @@
|
|||||||
<app-comic-artists-list />
|
<app-comic-artists-list />
|
||||||
</div>
|
</div>
|
||||||
<div>
|
<div>
|
||||||
|
@if (artist()) {
|
||||||
|
<h2>{{ artist().name }}</h2>
|
||||||
|
<a href="{{ artist().weblink }}">{{ artist().name }}</a>
|
||||||
|
} @else {
|
||||||
<h2>Artist Details</h2>
|
<h2>Artist Details</h2>
|
||||||
|
}
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
@@ -1,5 +1,8 @@
|
|||||||
import { Component } from '@angular/core';
|
import { Component, inject, input } from '@angular/core';
|
||||||
import { ComicArtistsListComponent } from '../comic-artists-list/comic-artists-list.component';
|
import { ComicArtistsListComponent } from '../comic-artists-list/comic-artists-list.component';
|
||||||
|
import { ArtistDetails } from './artist.model';
|
||||||
|
import { ActivatedRouteSnapshot, ResolveFn, RouterStateSnapshot } from '@angular/router';
|
||||||
|
import { ArtistService } from './artist.service';
|
||||||
|
|
||||||
@Component({
|
@Component({
|
||||||
selector: 'app-comic-artists',
|
selector: 'app-comic-artists',
|
||||||
@@ -8,5 +11,13 @@ import { ComicArtistsListComponent } from '../comic-artists-list/comic-artists-l
|
|||||||
styleUrl: './comic-artists.component.css'
|
styleUrl: './comic-artists.component.css'
|
||||||
})
|
})
|
||||||
export class ComicArtistsComponent {
|
export class ComicArtistsComponent {
|
||||||
|
artist = input.required<ArtistDetails>();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export const artistResolver: ResolveFn<ArtistDetails> = (route: ActivatedRouteSnapshot, state: RouterStateSnapshot) => {
|
||||||
|
const artistService = inject(ArtistService);
|
||||||
|
const artistId = route.paramMap.get('artistId');
|
||||||
|
const artistDetails = artistService.loadArtistDetails(artistId);
|
||||||
|
console.log(artistDetails);
|
||||||
|
return artistDetails;
|
||||||
|
};
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
import { Routes } from "@angular/router";
|
import { Routes } from "@angular/router";
|
||||||
import { ComicArtistsComponent } from "../comic-artists/comic-artists.component";
|
import { artistResolver, ComicArtistsComponent } from "../comic-artists/comic-artists.component";
|
||||||
import { ComicPublishersComponent } from './../comic-publishers/comic-publishers.component';
|
import { ComicPublishersComponent } from './../comic-publishers/comic-publishers.component';
|
||||||
import { ComicComicsComponent, comicResolver } from "../comic-comics/comic-comics.component";
|
import { ComicComicsComponent, comicResolver } from "../comic-comics/comic-comics.component";
|
||||||
|
|
||||||
@@ -19,10 +19,10 @@ export const comicRoutes: Routes = [
|
|||||||
path: 'publisher',
|
path: 'publisher',
|
||||||
component: ComicPublishersComponent
|
component: ComicPublishersComponent
|
||||||
},
|
},
|
||||||
// {
|
{
|
||||||
// path: 'publishers/:publisherId',
|
path: 'publishers/:publisherId',
|
||||||
// component: PublishersComponent,
|
component: ComicPublishersComponent,
|
||||||
// },
|
},
|
||||||
{
|
{
|
||||||
path: 'artist',
|
path: 'artist',
|
||||||
component: ComicArtistsComponent
|
component: ComicArtistsComponent
|
||||||
@@ -30,5 +30,8 @@ export const comicRoutes: Routes = [
|
|||||||
{
|
{
|
||||||
path: 'artist/:artistId',
|
path: 'artist/:artistId',
|
||||||
component: ComicArtistsComponent,
|
component: ComicArtistsComponent,
|
||||||
|
resolve: {
|
||||||
|
artist: artistResolver
|
||||||
|
}
|
||||||
},
|
},
|
||||||
];
|
];
|
||||||
|
|||||||
@@ -0,0 +1,17 @@
|
|||||||
|
<div>
|
||||||
|
<article>
|
||||||
|
<a [routerLink]="['/', 'media', 'file', mediafile().id]" routerLinkActive="active">
|
||||||
|
<span>{{ mediafile().title }}</span>
|
||||||
|
</a>
|
||||||
|
<a href="{{ mediafile().url }}"> >></a>
|
||||||
|
<p>
|
||||||
|
<span>
|
||||||
|
@if (mediafile().review) {
|
||||||
|
Review<img class="images" src="tick.png" />
|
||||||
|
} @else {
|
||||||
|
Review<img class="images" src="cross.png" />
|
||||||
|
}
|
||||||
|
</span>
|
||||||
|
</p>
|
||||||
|
</article>
|
||||||
|
</div>
|
||||||
@@ -0,0 +1,23 @@
|
|||||||
|
import { ComponentFixture, TestBed } from '@angular/core/testing';
|
||||||
|
|
||||||
|
import { MediaFileComponent } from './media-file.component';
|
||||||
|
|
||||||
|
describe('MediaFileComponent', () => {
|
||||||
|
let component: MediaFileComponent;
|
||||||
|
let fixture: ComponentFixture<MediaFileComponent>;
|
||||||
|
|
||||||
|
beforeEach(async () => {
|
||||||
|
await TestBed.configureTestingModule({
|
||||||
|
imports: [MediaFileComponent]
|
||||||
|
})
|
||||||
|
.compileComponents();
|
||||||
|
|
||||||
|
fixture = TestBed.createComponent(MediaFileComponent);
|
||||||
|
component = fixture.componentInstance;
|
||||||
|
fixture.detectChanges();
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should create', () => {
|
||||||
|
expect(component).toBeTruthy();
|
||||||
|
});
|
||||||
|
});
|
||||||
@@ -0,0 +1,13 @@
|
|||||||
|
import { Component, input } from '@angular/core';
|
||||||
|
import { MediaFile } from '../media-files/media-file.model';
|
||||||
|
import { RouterLink, RouterLinkActive } from '@angular/router';
|
||||||
|
|
||||||
|
@Component({
|
||||||
|
selector: 'app-media-file',
|
||||||
|
imports: [RouterLink, RouterLinkActive],
|
||||||
|
templateUrl: './media-file.component.html',
|
||||||
|
styleUrl: './media-file.component.css'
|
||||||
|
})
|
||||||
|
export class MediaFileComponent {
|
||||||
|
mediafile = input.required<MediaFile>();
|
||||||
|
}
|
||||||
@@ -0,0 +1,14 @@
|
|||||||
|
ul {
|
||||||
|
list-style: none;
|
||||||
|
margin: 0;
|
||||||
|
padding: 0;
|
||||||
|
display: flex;
|
||||||
|
gap: 0.5rem;
|
||||||
|
overflow: auto;
|
||||||
|
}
|
||||||
|
|
||||||
|
@media (min-width: 768px) {
|
||||||
|
ul {
|
||||||
|
flex-direction: column;
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,7 @@
|
|||||||
|
<ul>
|
||||||
|
@for (mediafile of files(); track mediafile.id) {
|
||||||
|
<li>
|
||||||
|
<app-media-file [mediafile]="mediafile" />
|
||||||
|
</li>
|
||||||
|
}
|
||||||
|
</ul>
|
||||||
+23
@@ -0,0 +1,23 @@
|
|||||||
|
import { ComponentFixture, TestBed } from '@angular/core/testing';
|
||||||
|
|
||||||
|
import { MediaFilesListComponent } from './media-files-list.component';
|
||||||
|
|
||||||
|
describe('MediaFilesListComponent', () => {
|
||||||
|
let component: MediaFilesListComponent;
|
||||||
|
let fixture: ComponentFixture<MediaFilesListComponent>;
|
||||||
|
|
||||||
|
beforeEach(async () => {
|
||||||
|
await TestBed.configureTestingModule({
|
||||||
|
imports: [MediaFilesListComponent]
|
||||||
|
})
|
||||||
|
.compileComponents();
|
||||||
|
|
||||||
|
fixture = TestBed.createComponent(MediaFilesListComponent);
|
||||||
|
component = fixture.componentInstance;
|
||||||
|
fixture.detectChanges();
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should create', () => {
|
||||||
|
expect(component).toBeTruthy();
|
||||||
|
});
|
||||||
|
});
|
||||||
@@ -0,0 +1,37 @@
|
|||||||
|
import { Component, DestroyRef, inject, OnInit, signal } from '@angular/core';
|
||||||
|
import { MediaFileComponent } from '../media-file/media-file.component';
|
||||||
|
import { MediaFileService } from '../media-files/media-file.service';
|
||||||
|
import { MediaFile } from '../media-files/media-file.model';
|
||||||
|
|
||||||
|
@Component({
|
||||||
|
selector: 'app-media-files-list',
|
||||||
|
imports: [MediaFileComponent],
|
||||||
|
templateUrl: './media-files-list.component.html',
|
||||||
|
styleUrl: './media-files-list.component.css'
|
||||||
|
})
|
||||||
|
export class MediaFilesListComponent implements OnInit {
|
||||||
|
files = signal<MediaFile[]>([]);
|
||||||
|
isFetching = signal(false);
|
||||||
|
error = signal('');
|
||||||
|
private mediaFileService = inject(MediaFileService);
|
||||||
|
private destroyRef = inject(DestroyRef);
|
||||||
|
|
||||||
|
ngOnInit() {
|
||||||
|
this.isFetching.set(true);
|
||||||
|
const subscription = this.mediaFileService.loadFiles().subscribe({
|
||||||
|
next: (files) => {
|
||||||
|
this.files.set(files);
|
||||||
|
},
|
||||||
|
error: (error: Error) => {
|
||||||
|
this.error.set(error.message);
|
||||||
|
},
|
||||||
|
complete: () => {
|
||||||
|
this.isFetching.set(false);
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
this.destroyRef.onDestroy(() => {
|
||||||
|
subscription.unsubscribe();
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,11 @@
|
|||||||
|
import { StreamingResourceOptions } from "@angular/core";
|
||||||
|
|
||||||
|
export interface MediaFile {
|
||||||
|
id: string;
|
||||||
|
title: string;
|
||||||
|
file_name: string;
|
||||||
|
cloud_link: string;
|
||||||
|
url: string;
|
||||||
|
review: boolean;
|
||||||
|
should_download: boolean;
|
||||||
|
}
|
||||||
@@ -0,0 +1,30 @@
|
|||||||
|
import { inject, Injectable, signal } from "@angular/core";
|
||||||
|
import { ErrorService } from "../../../shared/error.service";
|
||||||
|
import { HttpClient } from "@angular/common/http";
|
||||||
|
import { MediaFile } from "./media-file.model";
|
||||||
|
import { catchError, map, throwError } from "rxjs";
|
||||||
|
|
||||||
|
@Injectable({
|
||||||
|
providedIn: 'root',
|
||||||
|
})
|
||||||
|
export class MediaFileService {
|
||||||
|
private errorService = inject(ErrorService);
|
||||||
|
private httpClient = inject(HttpClient);
|
||||||
|
private files = signal<MediaFile[]>([]);
|
||||||
|
|
||||||
|
loadedFiles = this.files.asReadonly();
|
||||||
|
|
||||||
|
loadFiles() {
|
||||||
|
return this.fetchMediaFiles('http://127.0.0.1:8800/api/media/files', 'Someting went wrong fetching artists. Please try again later-');
|
||||||
|
}
|
||||||
|
|
||||||
|
private fetchMediaFiles(url: string, errorMessage: string) {
|
||||||
|
return this.httpClient.get<MediaFile[]>(url).pipe(
|
||||||
|
map((resData) => resData),
|
||||||
|
catchError((error) => {
|
||||||
|
console.log(error);
|
||||||
|
return throwError(() => new Error(errorMessage));
|
||||||
|
})
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,5 @@
|
|||||||
|
.grid-container {
|
||||||
|
display: grid;
|
||||||
|
grid-template-columns: 1fr 1fr;
|
||||||
|
grid-gap: 20px;
|
||||||
|
}
|
||||||
|
|||||||
@@ -1 +1,7 @@
|
|||||||
<p>media-files works!</p>
|
<div class="grid-container">
|
||||||
|
<div>
|
||||||
|
<app-media-files-list />
|
||||||
|
</div>
|
||||||
|
<div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|||||||
@@ -1,8 +1,9 @@
|
|||||||
import { Component } from '@angular/core';
|
import { Component } from '@angular/core';
|
||||||
|
import { MediaFilesListComponent } from '../media-files-list/media-files-list.component';
|
||||||
|
|
||||||
@Component({
|
@Component({
|
||||||
selector: 'app-media-files',
|
selector: 'app-media-files',
|
||||||
imports: [],
|
imports: [MediaFilesListComponent],
|
||||||
templateUrl: './media-files.component.html',
|
templateUrl: './media-files.component.html',
|
||||||
styleUrl: './media-files.component.css'
|
styleUrl: './media-files.component.css'
|
||||||
})
|
})
|
||||||
|
|||||||
@@ -8,12 +8,24 @@ export const mediaRoutes: Routes = [
|
|||||||
path: 'file',
|
path: 'file',
|
||||||
component: MediaFilesComponent
|
component: MediaFilesComponent
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
path: 'file/:fileId',
|
||||||
|
component: MediaFilesComponent,
|
||||||
|
},
|
||||||
{
|
{
|
||||||
path: 'actor',
|
path: 'actor',
|
||||||
component: MediaActorsComponent
|
component: MediaActorsComponent
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
path: 'actor/:actorId',
|
||||||
|
component: MediaActorsComponent,
|
||||||
|
},
|
||||||
{
|
{
|
||||||
path: 'video',
|
path: 'video',
|
||||||
component: MediaVideosComponent
|
component: MediaVideosComponent
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
path: 'video/:videoId',
|
||||||
|
component: MediaVideosComponent,
|
||||||
|
},
|
||||||
];
|
];
|
||||||
@@ -1,13 +1,15 @@
|
|||||||
from typing import List, AnyStr
|
from typing import List
|
||||||
from fastapi import APIRouter, HTTPException, status
|
from fastapi import APIRouter, HTTPException, status
|
||||||
|
|
||||||
from src.apis.utils import SessionDep
|
from src.apis.utils import SessionDep
|
||||||
from src.core.log_conf import logger
|
from src.core.log_conf import logger
|
||||||
from src.db.repository.comics.artist import get_artist_details
|
from src.db.repository.comics.artist import get_artist_details
|
||||||
from src.db.repository.comics.comic import list_comics, get_issue_details
|
from src.db.repository.comics.comic import get_comic_details, get_short_info, list_comics, get_issue_details
|
||||||
from src.schema.comics.comic import ComicResponse, ComicDetailsResponse, get_comic_details, get_short_info
|
from src.schema.comics.artist_details import ArtistDetailResponse
|
||||||
from src.schema.comics.artist import ArtistCreation, ArtistDetailResponse, ArtistResponse
|
from src.schema.comics.comic import ComicResponse
|
||||||
|
from src.schema.comics.artist import ArtistCreation, ArtistResponse
|
||||||
from src.db.models.comic import Comic, Artist, Issue
|
from src.db.models.comic import Comic, Artist, Issue
|
||||||
|
from src.schema.comics.comic_details import ComicDetailsResponse
|
||||||
from src.schema.comics.issue import IssueDetailsResponse
|
from src.schema.comics.issue import IssueDetailsResponse
|
||||||
|
|
||||||
router = APIRouter()
|
router = APIRouter()
|
||||||
@@ -23,7 +25,7 @@ def get_all_comics(db: SessionDep) -> List[ComicResponse]:
|
|||||||
return results
|
return results
|
||||||
|
|
||||||
@router.get("/comics/{comic_id}", response_model=ComicDetailsResponse)
|
@router.get("/comics/{comic_id}", response_model=ComicDetailsResponse)
|
||||||
def get_comic(comic_id: AnyStr, db: SessionDep) -> ComicDetailsResponse:
|
def get_comic(comic_id: str, db: SessionDep) -> ComicDetailsResponse:
|
||||||
comic = db.get(Comic, comic_id)
|
comic = db.get(Comic, comic_id)
|
||||||
if comic is None:
|
if comic is None:
|
||||||
raise HTTPException(status_code=404, detail="Comic could not be found")
|
raise HTTPException(status_code=404, detail="Comic could not be found")
|
||||||
@@ -41,7 +43,7 @@ def get_all_artists(db: SessionDep) -> List[ArtistResponse]:
|
|||||||
return results
|
return results
|
||||||
|
|
||||||
@router.get("/artists/{artist_id}", response_model=ArtistDetailResponse)
|
@router.get("/artists/{artist_id}", response_model=ArtistDetailResponse)
|
||||||
def get_artist(artist_id: AnyStr, db: SessionDep) -> ArtistDetailResponse:
|
def get_artist(artist_id: str, db: SessionDep) -> ArtistDetailResponse:
|
||||||
artist = db.get(Artist, artist_id)
|
artist = db.get(Artist, artist_id)
|
||||||
if artist is None:
|
if artist is None:
|
||||||
raise HTTPException(status_code=404, detail="Artist could not be found")
|
raise HTTPException(status_code=404, detail="Artist could not be found")
|
||||||
@@ -67,4 +69,3 @@ def get_issues(db: SessionDep) -> List[IssueDetailsResponse]:
|
|||||||
for issue in issues:
|
for issue in issues:
|
||||||
results.append(get_issue_details(issue))
|
results.append(get_issue_details(issue))
|
||||||
return results
|
return results
|
||||||
|
|
||||||
|
|||||||
@@ -1,19 +1,29 @@
|
|||||||
|
from typing import List
|
||||||
from src.db.models.comic import Artist
|
from src.db.models.comic import Artist
|
||||||
from src.schema.comics.artist import ArtistDetailResponse
|
from src.schema.comics.artist_details import ArtistDetailResponse, ArtistWorktypeComicResponse
|
||||||
|
from src.schema.comics.comic import ComicResponse
|
||||||
|
from src.schema.comics.worktype import WorktypeResponse
|
||||||
|
|
||||||
|
|
||||||
def get_artist_details(artist: Artist) -> ArtistDetailResponse:
|
def get_artist_details(artist: Artist) -> ArtistDetailResponse:
|
||||||
works = {}
|
works: List[ArtistWorktypeComicResponse] = []
|
||||||
|
works_map = {}
|
||||||
for work in artist.comic_works:
|
for work in artist.comic_works:
|
||||||
work_type = work.work_type.name
|
worktype_id = work.work_type.id
|
||||||
comic_title = work.comic.title
|
if worktype_id in works_map:
|
||||||
if work_type in works:
|
comic = ComicResponse(id=work.comic.id, title=work.comic.title, completed=work.comic.completed)
|
||||||
works[work_type].append(comic_title)
|
works_map[worktype_id].comics.append(comic)
|
||||||
else:
|
else:
|
||||||
works[work_type] = [comic_title]
|
works_map[worktype_id] = ArtistWorktypeComicResponse(
|
||||||
|
worktype=WorktypeResponse(id=worktype_id, name=work.work_type.name),
|
||||||
|
comics=[ComicResponse(id=work.comic.id, title=work.comic.title, completed=work.comic.completed)]
|
||||||
|
)
|
||||||
|
for value in works_map.values():
|
||||||
|
works.append(value)
|
||||||
response = ArtistDetailResponse(
|
response = ArtistDetailResponse(
|
||||||
id=artist.id,
|
id=artist.id,
|
||||||
name=artist.name,
|
name=artist.name,
|
||||||
|
weblink=artist.weblink,
|
||||||
works=works
|
works=works
|
||||||
)
|
)
|
||||||
return response
|
return response
|
||||||
|
|||||||
@@ -1,11 +1,15 @@
|
|||||||
from typing import List, Type, AnyStr
|
from typing import Dict, List
|
||||||
|
|
||||||
from sqlalchemy.orm import Session
|
from sqlalchemy.orm import Session
|
||||||
|
|
||||||
from src.core.log_conf import logger
|
from src.core.log_conf import logger
|
||||||
from src.db.models.comic import Comic, Issue
|
from src.db.models.comic import Comic, Issue
|
||||||
from src.schema.comics.comic import ComicSchema
|
from src.schema.comics.artist import ArtistResponse
|
||||||
|
from src.schema.comics.comic import ComicResponse, ComicSchema
|
||||||
|
from src.schema.comics.comic_details import ComicDetailsResponse, ComicWorktypeArtistResponse
|
||||||
from src.schema.comics.issue import IssueDetailsResponse
|
from src.schema.comics.issue import IssueDetailsResponse
|
||||||
|
from src.schema.comics.volume import VolumeResponse
|
||||||
|
from src.schema.comics.worktype import WorktypeResponse
|
||||||
|
|
||||||
|
|
||||||
def list_comics(db: Session) -> List[Comic]:
|
def list_comics(db: Session) -> List[Comic]:
|
||||||
@@ -25,7 +29,48 @@ def get_issue_details(issue: Issue) -> IssueDetailsResponse:
|
|||||||
return response
|
return response
|
||||||
|
|
||||||
|
|
||||||
def update_comic(comic: ComicSchema, comic_id: AnyStr, db: Session) -> type[Comic] | None:
|
def update_comic(comic: ComicSchema, comic_id: str, db: Session) -> type[Comic] | None:
|
||||||
logger.info(f"update_comic: {comic} with {comic_id}")
|
logger.info(f"update_comic: {comic} with {comic_id}")
|
||||||
comic = db.get(Comic, comic_id) # type: ignore
|
comic = db.get(Comic, comic_id) # type: ignore
|
||||||
return comic # type: ignore
|
return comic # type: ignore
|
||||||
|
|
||||||
|
def get_short_info(comic: Comic) -> ComicResponse:
|
||||||
|
response = ComicResponse(
|
||||||
|
id=comic.id,
|
||||||
|
title=str(comic.title),
|
||||||
|
completed=bool(comic.completed == 1)
|
||||||
|
)
|
||||||
|
return response
|
||||||
|
|
||||||
|
def get_comic_details(comic: Comic) -> ComicDetailsResponse:
|
||||||
|
volumes: List[VolumeResponse] = []
|
||||||
|
for volume in comic.volumes:
|
||||||
|
volumes.append(VolumeResponse(id=volume.id, name=volume.name))
|
||||||
|
works: List[ComicWorktypeArtistResponse] = []
|
||||||
|
works_map: Dict[str, ComicWorktypeArtistResponse] = {}
|
||||||
|
for work in comic.comic_works:
|
||||||
|
worktype_id = work.work_type.id
|
||||||
|
if worktype_id in works_map:
|
||||||
|
artist = ArtistResponse(id=work.artist.id, name=work.artist.name)
|
||||||
|
works_map[worktype_id].artists.append(artist)
|
||||||
|
logger.info(f"add artist to response map: {artist} -> {works_map}")
|
||||||
|
print(f"add artist to response map: {artist} -> {works_map}")
|
||||||
|
else:
|
||||||
|
works_map[worktype_id] = ComicWorktypeArtistResponse(
|
||||||
|
worktype=WorktypeResponse(id=worktype_id, name=work.work_type.name),
|
||||||
|
artists=[ArtistResponse(id=work.artist.id, name=work.artist.name)]
|
||||||
|
)
|
||||||
|
for value in works_map.values():
|
||||||
|
works.append(value)
|
||||||
|
response = ComicDetailsResponse(
|
||||||
|
id=comic.id,
|
||||||
|
created=str(comic.created_date),
|
||||||
|
title=str(comic.title),
|
||||||
|
completed=bool(comic.completed),
|
||||||
|
current_order=bool(comic.current_order),
|
||||||
|
weblink=str(comic.weblink),
|
||||||
|
publisher=comic.publisher.name,
|
||||||
|
volumes=volumes,
|
||||||
|
works=works
|
||||||
|
)
|
||||||
|
return response
|
||||||
|
|||||||
@@ -1,9 +1,6 @@
|
|||||||
from typing import List, Dict
|
|
||||||
|
|
||||||
from pydantic import BaseModel
|
from pydantic import BaseModel
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
class ArtistCreation(BaseModel):
|
class ArtistCreation(BaseModel):
|
||||||
id: str
|
id: str
|
||||||
name: str
|
name: str
|
||||||
@@ -11,9 +8,3 @@ class ArtistCreation(BaseModel):
|
|||||||
class ArtistResponse(BaseModel):
|
class ArtistResponse(BaseModel):
|
||||||
id: str
|
id: str
|
||||||
name: str
|
name: str
|
||||||
|
|
||||||
class ArtistDetailResponse(BaseModel):
|
|
||||||
id: str
|
|
||||||
name: str
|
|
||||||
weblink: str
|
|
||||||
works: Dict[str, List[str]]
|
|
||||||
|
|||||||
@@ -0,0 +1,16 @@
|
|||||||
|
from typing import List
|
||||||
|
from pydantic import BaseModel
|
||||||
|
|
||||||
|
from src.schema.comics.comic import ComicResponse
|
||||||
|
from src.schema.comics.worktype import WorktypeResponse
|
||||||
|
|
||||||
|
|
||||||
|
class ArtistWorktypeComicResponse(BaseModel):
|
||||||
|
worktype: WorktypeResponse
|
||||||
|
comics: List[ComicResponse]
|
||||||
|
|
||||||
|
class ArtistDetailResponse(BaseModel):
|
||||||
|
id: str
|
||||||
|
name: str
|
||||||
|
weblink: str
|
||||||
|
works: List[ArtistWorktypeComicResponse]
|
||||||
@@ -1,12 +1,6 @@
|
|||||||
from typing import List, Dict, Optional
|
from typing import Optional
|
||||||
|
|
||||||
from pydantic import BaseModel, AnyUrl
|
from pydantic import BaseModel, AnyUrl
|
||||||
|
|
||||||
from src.core.log_conf import logger
|
from src.core.log_conf import logger
|
||||||
from src.db.models.comic import Comic
|
|
||||||
from src.schema.comics.artist import ArtistResponse
|
|
||||||
from src.schema.comics.volume import VolumeResponse
|
|
||||||
from src.schema.comics.worktype import WorktypeResponse
|
|
||||||
|
|
||||||
|
|
||||||
class ComicResponse(BaseModel):
|
class ComicResponse(BaseModel):
|
||||||
@@ -15,70 +9,9 @@ class ComicResponse(BaseModel):
|
|||||||
completed: bool
|
completed: bool
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
class ComicWorktypeArtistResponse(BaseModel):
|
|
||||||
worktype: WorktypeResponse
|
|
||||||
artists: List[ArtistResponse]
|
|
||||||
|
|
||||||
|
|
||||||
class ComicDetailsResponse(BaseModel):
|
|
||||||
id: str
|
|
||||||
created: str
|
|
||||||
title: str
|
|
||||||
completed : bool
|
|
||||||
current_order : bool
|
|
||||||
weblink: str
|
|
||||||
publisher: str
|
|
||||||
volumes: List[VolumeResponse]
|
|
||||||
works: List[ComicWorktypeArtistResponse]
|
|
||||||
|
|
||||||
|
|
||||||
class ComicSchema(BaseModel):
|
class ComicSchema(BaseModel):
|
||||||
id: str
|
id: str
|
||||||
title: str
|
title: str
|
||||||
weblink: Optional[AnyUrl]
|
weblink: Optional[AnyUrl]
|
||||||
completed: Optional[bool]
|
completed: Optional[bool]
|
||||||
current_order: Optional[bool]
|
current_order: Optional[bool]
|
||||||
|
|
||||||
|
|
||||||
def get_short_info(comic: Comic) -> ComicResponse:
|
|
||||||
response = ComicResponse(
|
|
||||||
id=comic.id,
|
|
||||||
title=str(comic.title),
|
|
||||||
completed=bool(comic.completed == 1)
|
|
||||||
)
|
|
||||||
return response
|
|
||||||
|
|
||||||
|
|
||||||
def get_comic_details(comic: Comic) -> ComicDetailsResponse:
|
|
||||||
volumes: List[VolumeResponse] = []
|
|
||||||
for volume in comic.volumes:
|
|
||||||
volumes.append(VolumeResponse(id=volume.id, name=volume.name))
|
|
||||||
works: List[ComicWorktypeArtistResponse] = []
|
|
||||||
works_map: Dict[str, ComicWorktypeArtistResponse] = {}
|
|
||||||
for work in comic.comic_works:
|
|
||||||
worktype_id = work.work_type.id
|
|
||||||
if worktype_id in works_map:
|
|
||||||
artist = ArtistResponse(id=work.artist.id, name=work.artist.name)
|
|
||||||
works_map[worktype_id].artists.append(artist)
|
|
||||||
logger.info(f"add artist to response map: {artist} -> {works_map}")
|
|
||||||
print(f"add artist to response map: {artist} -> {works_map}")
|
|
||||||
else:
|
|
||||||
works_map[worktype_id] = ComicWorktypeArtistResponse(
|
|
||||||
worktype=WorktypeResponse(id=worktype_id, name=work.work_type.name),
|
|
||||||
artists=[ArtistResponse(id=work.artist.id, name=work.artist.name)]
|
|
||||||
)
|
|
||||||
for value in works_map.values():
|
|
||||||
works.append(value)
|
|
||||||
response = ComicDetailsResponse(
|
|
||||||
id=comic.id,
|
|
||||||
created=str(comic.created_date),
|
|
||||||
title=str(comic.title),
|
|
||||||
completed=bool(comic.completed),
|
|
||||||
current_order=bool(comic.current_order),
|
|
||||||
weblink=str(comic.weblink),
|
|
||||||
publisher=comic.publisher.name,
|
|
||||||
volumes=volumes,
|
|
||||||
works=works
|
|
||||||
)
|
|
||||||
return response
|
|
||||||
|
|||||||
@@ -0,0 +1,23 @@
|
|||||||
|
from typing import List
|
||||||
|
from pydantic import BaseModel
|
||||||
|
|
||||||
|
from src.schema.comics.artist import ArtistResponse
|
||||||
|
from src.schema.comics.volume import VolumeResponse
|
||||||
|
from src.schema.comics.worktype import WorktypeResponse
|
||||||
|
|
||||||
|
|
||||||
|
class ComicWorktypeArtistResponse(BaseModel):
|
||||||
|
worktype: WorktypeResponse
|
||||||
|
artists: List[ArtistResponse]
|
||||||
|
|
||||||
|
|
||||||
|
class ComicDetailsResponse(BaseModel):
|
||||||
|
id: str
|
||||||
|
created: str
|
||||||
|
title: str
|
||||||
|
completed : bool
|
||||||
|
current_order : bool
|
||||||
|
weblink: str
|
||||||
|
publisher: str
|
||||||
|
volumes: List[VolumeResponse]
|
||||||
|
works: List[ComicWorktypeArtistResponse]
|
||||||
Reference in New Issue
Block a user