First commit
This commit is contained in:
@@ -0,0 +1,10 @@
|
|||||||
|
# Seuils et Marges (en mm)
|
||||||
|
MINIMUM_POLICE_SIZE=9
|
||||||
|
MARGIN_HEIGHT_BOTTOM=15
|
||||||
|
MARGIN_LEFT_RIGHT=20
|
||||||
|
|
||||||
|
# Couleurs (Hexadécimal)
|
||||||
|
COLOR_PRIMARY=#1A365D
|
||||||
|
COLOR_SECONDARY=#2B6CB0
|
||||||
|
COLOR_TEXT=#2D3748
|
||||||
|
COLOR_ACCENT=#4A5568
|
||||||
+10
@@ -0,0 +1,10 @@
|
|||||||
|
# Python-generated files
|
||||||
|
__pycache__/
|
||||||
|
*.py[oc]
|
||||||
|
build/
|
||||||
|
dist/
|
||||||
|
wheels/
|
||||||
|
*.egg-info
|
||||||
|
|
||||||
|
# Virtual environments
|
||||||
|
.venv
|
||||||
@@ -0,0 +1 @@
|
|||||||
|
3.14
|
||||||
@@ -0,0 +1,26 @@
|
|||||||
|
import os
|
||||||
|
from dotenv import load_dotenv
|
||||||
|
|
||||||
|
load_dotenv()
|
||||||
|
|
||||||
|
# Dimensions & Marges
|
||||||
|
MINIMUM_POLICE_SIZE = float(os.getenv("MINIMUM_POLICE_SIZE", "7.5"))
|
||||||
|
MARGIN_Y_MM = float(os.getenv("MARGIN_HEIGHT_BOTTOM", "10"))
|
||||||
|
MARGIN_X_MM = float(os.getenv("MARGIN_LEFT_RIGHT", "12"))
|
||||||
|
|
||||||
|
# Conversions mm -> points PDF (1 mm ≈ 2.83465 pt)
|
||||||
|
MM_TO_PT = 2.83465
|
||||||
|
PAGE_WIDTH_PT = 210 * MM_TO_PT
|
||||||
|
PAGE_HEIGHT_PT = 297 * MM_TO_PT
|
||||||
|
|
||||||
|
MARGIN_X_PT = MARGIN_X_MM * MM_TO_PT
|
||||||
|
MARGIN_Y_PT = MARGIN_Y_MM * MM_TO_PT
|
||||||
|
|
||||||
|
USABLE_WIDTH = PAGE_WIDTH_PT - (2 * MARGIN_X_PT)
|
||||||
|
USABLE_HEIGHT = PAGE_HEIGHT_PT - (2 * MARGIN_Y_PT)
|
||||||
|
|
||||||
|
# Couleurs
|
||||||
|
COLOR_PRIMARY = os.getenv("COLOR_PRIMARY", "#1A365D")
|
||||||
|
COLOR_SECONDARY = os.getenv("COLOR_SECONDARY", "#2B6CB0")
|
||||||
|
COLOR_TEXT = os.getenv("COLOR_TEXT", "#2D3748")
|
||||||
|
COLOR_ACCENT = os.getenv("COLOR_ACCENT", "#4A5568")
|
||||||
@@ -0,0 +1,4 @@
|
|||||||
|
{
|
||||||
|
"title": "Développeur Full Stack & Lead Tech",
|
||||||
|
"summary": "Ingénieur logiciel passionné avec plus de 6 ans d'expérience dans la conception d'architectures résilientes et évolutives. Spécialisé en écosystèmes Python, backend, et optimisation de performances. Fort intérêt pour le Clean Code et le mentoring technique."
|
||||||
|
}
|
||||||
@@ -0,0 +1,20 @@
|
|||||||
|
[
|
||||||
|
{
|
||||||
|
"role": "Senior Python Engineer",
|
||||||
|
"company": "Tech Corp",
|
||||||
|
"dates": "2022 - Présent",
|
||||||
|
"details": [
|
||||||
|
"Conception et déploiement de microservices traitant +1M de requêtes/jour.",
|
||||||
|
"Réduction de la latence backend de 35% grâce à l'optimisation des requêtes SQL et du caching."
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"role": "Développeur Software",
|
||||||
|
"company": "Data Solutions",
|
||||||
|
"dates": "2019 - 2022",
|
||||||
|
"details": [
|
||||||
|
"Développement de pipelines ETL automatisés en Python.",
|
||||||
|
"Collaboration au sein d'une équipe Agile de 8 développeurs."
|
||||||
|
]
|
||||||
|
}
|
||||||
|
]
|
||||||
@@ -0,0 +1,5 @@
|
|||||||
|
[
|
||||||
|
"Contribution Open Source",
|
||||||
|
"Échecs en compétition",
|
||||||
|
"Randonnée"
|
||||||
|
]
|
||||||
@@ -0,0 +1,5 @@
|
|||||||
|
{
|
||||||
|
"LinkedIn": "https://linkedin.com/in/exemple",
|
||||||
|
"GitHub": "https://github.com/exemple",
|
||||||
|
"Portfolio": "https://alexandre-dupont.dev"
|
||||||
|
}
|
||||||
@@ -0,0 +1,7 @@
|
|||||||
|
{
|
||||||
|
"first_name": "Alexandre",
|
||||||
|
"last_name": "Dupont",
|
||||||
|
"email": "alexandre.dupont@email.com",
|
||||||
|
"phone": "+33 6 12 34 56 78",
|
||||||
|
"location": "Paris, France"
|
||||||
|
}
|
||||||
@@ -0,0 +1,5 @@
|
|||||||
|
{
|
||||||
|
"Français": "Langue maternelle",
|
||||||
|
"Anglais": "Courant (C1 / TOEIC 940)",
|
||||||
|
"Espagnol": "Notions"
|
||||||
|
}
|
||||||
@@ -0,0 +1,104 @@
|
|||||||
|
%PDF-1.4
|
||||||
|
%“Œ‹ž ReportLab Generated PDF document (opensource)
|
||||||
|
1 0 obj
|
||||||
|
<<
|
||||||
|
/F1 2 0 R /F2 3 0 R /F3 4 0 R
|
||||||
|
>>
|
||||||
|
endobj
|
||||||
|
2 0 obj
|
||||||
|
<<
|
||||||
|
/BaseFont /Helvetica /Encoding /WinAnsiEncoding /Name /F1 /Subtype /Type1 /Type /Font
|
||||||
|
>>
|
||||||
|
endobj
|
||||||
|
3 0 obj
|
||||||
|
<<
|
||||||
|
/BaseFont /Helvetica-Bold /Encoding /WinAnsiEncoding /Name /F2 /Subtype /Type1 /Type /Font
|
||||||
|
>>
|
||||||
|
endobj
|
||||||
|
4 0 obj
|
||||||
|
<<
|
||||||
|
/BaseFont /Helvetica-Oblique /Encoding /WinAnsiEncoding /Name /F3 /Subtype /Type1 /Type /Font
|
||||||
|
>>
|
||||||
|
endobj
|
||||||
|
5 0 obj
|
||||||
|
<<
|
||||||
|
/A <<
|
||||||
|
/S /URI /Type /Action /URI (https://linkedin.com/in/exemple)
|
||||||
|
>> /Border [ 0 0 0 ] /Rect [ 283.437 751.5733 314.055 761.2933 ] /Subtype /Link /Type /Annot
|
||||||
|
>>
|
||||||
|
endobj
|
||||||
|
6 0 obj
|
||||||
|
<<
|
||||||
|
/A <<
|
||||||
|
/S /URI /Type /Action /URI (https://github.com/exemple)
|
||||||
|
>> /Border [ 0 0 0 ] /Rect [ 321.3936 751.5733 346.6008 761.2933 ] /Subtype /Link /Type /Annot
|
||||||
|
>>
|
||||||
|
endobj
|
||||||
|
7 0 obj
|
||||||
|
<<
|
||||||
|
/A <<
|
||||||
|
/S /URI /Type /Action /URI (https://alexandre-dupont.dev)
|
||||||
|
>> /Border [ 0 0 0 ] /Rect [ 353.9394 751.5733 383.6502 761.2933 ] /Subtype /Link /Type /Annot
|
||||||
|
>>
|
||||||
|
endobj
|
||||||
|
8 0 obj
|
||||||
|
<<
|
||||||
|
/Annots [ 5 0 R 6 0 R 7 0 R ] /Contents 12 0 R /MediaBox [ 0 0 595.2756 841.8898 ] /Parent 11 0 R /Resources <<
|
||||||
|
/Font 1 0 R /ProcSet [ /PDF /Text /ImageB /ImageC /ImageI ]
|
||||||
|
>> /Rotate 0
|
||||||
|
/Trans <<
|
||||||
|
|
||||||
|
>> /Type /Page
|
||||||
|
>>
|
||||||
|
endobj
|
||||||
|
9 0 obj
|
||||||
|
<<
|
||||||
|
/PageMode /UseNone /Pages 11 0 R /Type /Catalog
|
||||||
|
>>
|
||||||
|
endobj
|
||||||
|
10 0 obj
|
||||||
|
<<
|
||||||
|
/Author (\(anonymous\)) /CreationDate (D:20260724204228+02'00') /Creator (\(unspecified\)) /Keywords () /ModDate (D:20260724204228+02'00') /Producer (ReportLab PDF Library - \(opensource\))
|
||||||
|
/Subject (\(unspecified\)) /Title (\(anonymous\)) /Trapped /False
|
||||||
|
>>
|
||||||
|
endobj
|
||||||
|
11 0 obj
|
||||||
|
<<
|
||||||
|
/Count 1 /Kids [ 8 0 R ] /Type /Pages
|
||||||
|
>>
|
||||||
|
endobj
|
||||||
|
12 0 obj
|
||||||
|
<<
|
||||||
|
/Filter [ /ASCII85Decode /FlateDecode ] /Length 1447
|
||||||
|
>>
|
||||||
|
stream
|
||||||
|
Gau0B9lo&I&A@C2m%q,<,VT8gZ_ojim&Hi'NlB<9TF4GqA],]VK:nG+q[Pk_OWA"D\*c['JZIV9]C.n763InIn6+9A+%eLVUHf]]2aCjc6TKj?3Og1'0biW'6o<"/b@?\MS3p9emK\D7Zq)VW7VS/85^42[;$qJ(pe\-bqp1WT$'[:eT/!!!$"Xqu=lS`U:DA6iH>*sd]>eOY#0K_<ro*s+*.6NH,#&?tJp:7OP>I'Ird3?8N.!]Qdl_1ZOI5Qk'gU3a@gBVtiTYqAoDt4k)XK#FF\9L*M<YS"L+/!+PZZ`_7[6eWA3652NO(=-,gB71/8%*(E-4@k^g^&J7N##8;3jHV/;m*<MsZ@Q\W=o_iE;IrMrRZ,P!""0`[N8=j69<@bXot;GW5'<S/@)b5H0J6J1rJ`3MHDUp4N+W&t<a#A_>RP=:;*!ASP>o/;(F%6gCYh@n9M=o)AD^=P:E@1FmR$rDAsGE%:N$/gn71[JT#i]&1-cn'<YHFZ2c]F*K!N[U%#SkK2#l."!(4pg`e<WeApu">qG]+Y8Ws$BtESEL)]RjtDrkYDT_e_D3Os#j(XBibUA5fQEW!,X"]:mg@+iaqT_0R$+$Y_hh]FM.l/8U!-,0&f!6tD5GY656CA*'f;q+@>U(tcn-f0n4Vd`>)@^m97NlC<(9sA3,(3#O+aWQV8LGu(0e",]Tl*`G1ZjW#gtQ0F7K?9ZKZcs%uDecP\`^8e021SmFTo@GS8QZ_W=jCC\0]u(=aGjjZB*]1euW-'n(L`#47DT[ARS/i65:.;YpmPK4,6.j-rLT.23[iAr/^26E!aCA#3LK;)t^q[Cksi**uNHOQ'9n1&h&j_(!'dN+\pU*'@BDoD.HV2B,Gij8!+O8\.9Y-Y:nAZ!i<h?Q=*D3GN9pH3l:V/#a+t^p3"R&/L49_-g\^C]fS=M\q*@E7o:5[9L:(N85F&I50j8D=(bZYaa$!U8!::a3uF]d4a?TVj<T*/_UG4@11?fl%F6LQN$-@>.j;*K<RN<A30Sr:@RHbDig</ETM0n]4s]X,sVbQ'Y2eK5NU!_Yd+WX1N1T%N^+UJO\37?'$NR8_c/[q7!1$Ql5BQJFGKIk^p#1>m^]lJT7t7t'K[%)^cXT5d;@eZ/`8d:\ikgW&cXeeH=/\D%;].sa9IpZfYY.gK'8`(L$N$IoetFfl0u'D,5PY'Tp3&F[O6dmTLJ*n][NH1$,,e<K5e,Z$@O^U0:V?hg1T?hU.:FHn'3mUR@XBN$jTIhChI4\^e]I7=n"BaZMjGF.AoM93eUZrZl54`]bG=I%o;gimJHY!FOkf`@Q:*$%->HQ14g^1(2roD)7PT-BqrDc1G_<,LI]7g='j$_4=dVTF#Z=Obf]G7>&_X,_amo-]!NLrmB!r1^&SrYC7(L9DD%n0=`oX&OBuM`E:7a$"S_U>oQ#?dlAasGGSp>\3UfNd~>endstream
|
||||||
|
endobj
|
||||||
|
xref
|
||||||
|
0 13
|
||||||
|
0000000000 65535 f
|
||||||
|
0000000061 00000 n
|
||||||
|
0000000112 00000 n
|
||||||
|
0000000219 00000 n
|
||||||
|
0000000331 00000 n
|
||||||
|
0000000446 00000 n
|
||||||
|
0000000627 00000 n
|
||||||
|
0000000805 00000 n
|
||||||
|
0000000985 00000 n
|
||||||
|
0000001220 00000 n
|
||||||
|
0000001289 00000 n
|
||||||
|
0000001570 00000 n
|
||||||
|
0000001630 00000 n
|
||||||
|
trailer
|
||||||
|
<<
|
||||||
|
/ID
|
||||||
|
[<1760f8d72da58443956870aec1ff8660><1760f8d72da58443956870aec1ff8660>]
|
||||||
|
% ReportLab generated PDF document -- digest (opensource)
|
||||||
|
|
||||||
|
/Info 10 0 R
|
||||||
|
/Root 9 0 R
|
||||||
|
/Size 13
|
||||||
|
>>
|
||||||
|
startxref
|
||||||
|
3169
|
||||||
|
%%EOF
|
||||||
@@ -0,0 +1,104 @@
|
|||||||
|
%PDF-1.4
|
||||||
|
%“Œ‹ž ReportLab Generated PDF document (opensource)
|
||||||
|
1 0 obj
|
||||||
|
<<
|
||||||
|
/F1 2 0 R /F2 3 0 R /F3 4 0 R
|
||||||
|
>>
|
||||||
|
endobj
|
||||||
|
2 0 obj
|
||||||
|
<<
|
||||||
|
/BaseFont /Helvetica /Encoding /WinAnsiEncoding /Name /F1 /Subtype /Type1 /Type /Font
|
||||||
|
>>
|
||||||
|
endobj
|
||||||
|
3 0 obj
|
||||||
|
<<
|
||||||
|
/BaseFont /Helvetica-Bold /Encoding /WinAnsiEncoding /Name /F2 /Subtype /Type1 /Type /Font
|
||||||
|
>>
|
||||||
|
endobj
|
||||||
|
4 0 obj
|
||||||
|
<<
|
||||||
|
/BaseFont /Helvetica-Oblique /Encoding /WinAnsiEncoding /Name /F3 /Subtype /Type1 /Type /Font
|
||||||
|
>>
|
||||||
|
endobj
|
||||||
|
5 0 obj
|
||||||
|
<<
|
||||||
|
/A <<
|
||||||
|
/S /URI /Type /Action /URI (https://linkedin.com/in/exemple)
|
||||||
|
>> /Border [ 0 0 0 ] /Rect [ 306.1142 737.4 336.7322 747.12 ] /Subtype /Link /Type /Annot
|
||||||
|
>>
|
||||||
|
endobj
|
||||||
|
6 0 obj
|
||||||
|
<<
|
||||||
|
/A <<
|
||||||
|
/S /URI /Type /Action /URI (https://github.com/exemple)
|
||||||
|
>> /Border [ 0 0 0 ] /Rect [ 344.0708 737.4 369.278 747.12 ] /Subtype /Link /Type /Annot
|
||||||
|
>>
|
||||||
|
endobj
|
||||||
|
7 0 obj
|
||||||
|
<<
|
||||||
|
/A <<
|
||||||
|
/S /URI /Type /Action /URI (https://alexandre-dupont.dev)
|
||||||
|
>> /Border [ 0 0 0 ] /Rect [ 376.6166 737.4 406.3274 747.12 ] /Subtype /Link /Type /Annot
|
||||||
|
>>
|
||||||
|
endobj
|
||||||
|
8 0 obj
|
||||||
|
<<
|
||||||
|
/Annots [ 5 0 R 6 0 R 7 0 R ] /Contents 12 0 R /MediaBox [ 0 0 595.2756 841.8898 ] /Parent 11 0 R /Resources <<
|
||||||
|
/Font 1 0 R /ProcSet [ /PDF /Text /ImageB /ImageC /ImageI ]
|
||||||
|
>> /Rotate 0
|
||||||
|
/Trans <<
|
||||||
|
|
||||||
|
>> /Type /Page
|
||||||
|
>>
|
||||||
|
endobj
|
||||||
|
9 0 obj
|
||||||
|
<<
|
||||||
|
/PageMode /UseNone /Pages 11 0 R /Type /Catalog
|
||||||
|
>>
|
||||||
|
endobj
|
||||||
|
10 0 obj
|
||||||
|
<<
|
||||||
|
/Author (\(anonymous\)) /CreationDate (D:20260724204357+02'00') /Creator (\(unspecified\)) /Keywords () /ModDate (D:20260724204357+02'00') /Producer (ReportLab PDF Library - \(opensource\))
|
||||||
|
/Subject (\(unspecified\)) /Title (\(anonymous\)) /Trapped /False
|
||||||
|
>>
|
||||||
|
endobj
|
||||||
|
11 0 obj
|
||||||
|
<<
|
||||||
|
/Count 1 /Kids [ 8 0 R ] /Type /Pages
|
||||||
|
>>
|
||||||
|
endobj
|
||||||
|
12 0 obj
|
||||||
|
<<
|
||||||
|
/Filter [ /ASCII85Decode /FlateDecode ] /Length 1444
|
||||||
|
>>
|
||||||
|
stream
|
||||||
|
Gau0B9lo&I&A@C2m%q,F,VT8g6gUe4/A(2GZ=C-q4m2rGCDei4clhNts1SS3?ZFS(e%-?1ZrgcZG2IWLKa8lqiE91]mgD+eUd,e3pHW4$6SX3b0mFUY3>>@\d[W[j73lG@<eI:NTb5OJ$A;9FV$R^)``\OX>n>d.S'Cmf-:n8-"_X/1k_Om1N0,[]7>Bt457>01#T%>pZoa!$,8<B<l5p3PNZ^k@Tc4,AQ/NufTpbqOFk'r.MpQR1Z4':JGf"JDC$pP)?,Q/@aV4N0b1&;W(+rgf@,XNL#X?07`<lr'+*o+#PiR+qm7N\a=m/J@N`%!9`-B/Xp1g7O`LOl*18#ne/:e.1(6aR;]82&mcPo`uZY["pj&&F3;=\iJVjuh,V[$oj-Rk,:_n<1a`(7hX5"3C;KMHM(&_aYX3-a=1YoON]KgGZhQ&plfVLg,=13g\sUn0n8L*VIhe:hgoQ-EOCZpL,3KgJ"Z1T;I92:^Nt_j)b:IVnPfNSbT=mZ!C,IirPhOJAl(IU!RN%4M-4pcFXlZ2*!%LsqlQ7X5><B^>32PS+`Zh^=L\$QFYM3DdGTa3F#Ej`K'ti&"(tgrI3Kb/V')'_5(dP)^0mWEsp_B^&NjbZ^;F("AslUs[mLJU#0IC=P5H6:aE+#$*,H\b:q^U7_/%#F0hfkRqYYNJ>u?YiULZ=M!'m"4jA/;G#@J)o/$DLI%=t[8&nq6XUKa/u;CM$2%._Lm[<tj-Dfa.W"&*MgI/S/_l3R&bYE@TEA+S=ij-ToatrGpVp7*Nt$Y5@i\,-GG#ucF&3g*BS<WWTm(b$Aqb[qKi"Id`_*oKLZO/S:G%ad&L@b*?BZ7j5AujXdbF"s5+X=@'4nIO9X!XNa_Wk<o\t_\Sh_sVDB*-/#%'O^Z\J^!C$]F30[X[8&T'e<j4=j.N+Kh(@;K`hb9;c\K6M:`nGot$T(X@@q4Yh-+k\f03l:RJ0]P;bdD<u#WKiV08*rlHajQU^mB\@K!G@+.69BEZJ+ouZTA%($CsG55f(QG$-N0QqmleRj8%iZ\/^]g`-!X:+7Lq]*P941LQTSm:O*M^r3dDm2k3tX?pb3(=BqZ7&K@K5ddm\*!oiL)&Mm)cigq-A!i_a`%#Ua!lI)G.p0T>/`%Qr/8\Wr-EIpo-9F#eogj\maH<)R3.P@=`jFf#eSF10(m>Gn7Q:+'3@IqV+sEG')X#LPkjdQ;ZR4^f!"eB/]gHn?*0UPq7Em,ldhUJB7T0)p,KS=S!0roj*h,j+uA]`KkcV.Rj09ZZAZQXlTm]l"E3iQu==lL=5JgO+7KfBV#CA(+WM0?`=A_AO'>PN`qT3*Mmph.G*b@]bXdAtQU?1?`Q`j0(P+nRPt]:)7eg17]6?1UZ;.'tlS?oW!!S)ss`>$XNhb2/66\eo'rM*`r/fAIr*\Wc5jp1*h'u.eIIsh<E-q%ikk(!W@:/JH~>endstream
|
||||||
|
endobj
|
||||||
|
xref
|
||||||
|
0 13
|
||||||
|
0000000000 65535 f
|
||||||
|
0000000061 00000 n
|
||||||
|
0000000112 00000 n
|
||||||
|
0000000219 00000 n
|
||||||
|
0000000331 00000 n
|
||||||
|
0000000446 00000 n
|
||||||
|
0000000624 00000 n
|
||||||
|
0000000796 00000 n
|
||||||
|
0000000971 00000 n
|
||||||
|
0000001206 00000 n
|
||||||
|
0000001275 00000 n
|
||||||
|
0000001556 00000 n
|
||||||
|
0000001616 00000 n
|
||||||
|
trailer
|
||||||
|
<<
|
||||||
|
/ID
|
||||||
|
[<1148cf4482e907e3947d7ba54d2ab124><1148cf4482e907e3947d7ba54d2ab124>]
|
||||||
|
% ReportLab generated PDF document -- digest (opensource)
|
||||||
|
|
||||||
|
/Info 10 0 R
|
||||||
|
/Root 9 0 R
|
||||||
|
/Size 13
|
||||||
|
>>
|
||||||
|
startxref
|
||||||
|
3152
|
||||||
|
%%EOF
|
||||||
@@ -0,0 +1,40 @@
|
|||||||
|
import json
|
||||||
|
from pathlib import Path
|
||||||
|
|
||||||
|
DATA_DIR = Path(__file__).parent / "data"
|
||||||
|
|
||||||
|
REQUIRED_FILES = {
|
||||||
|
"identity.json": ["first_name", "last_name", "email"],
|
||||||
|
"context.json": ["title", "summary"],
|
||||||
|
"experiences.json": [],
|
||||||
|
"languages.json": [],
|
||||||
|
"hobbies.json": [],
|
||||||
|
"hyperlinks.json": []
|
||||||
|
}
|
||||||
|
|
||||||
|
def load_json_data() -> dict:
|
||||||
|
data = {}
|
||||||
|
if not DATA_DIR.exists():
|
||||||
|
raise FileNotFoundError(f"Le dossier '{DATA_DIR}' n'existe pas.")
|
||||||
|
|
||||||
|
for filename, required_keys in REQUIRED_FILES.items():
|
||||||
|
file_path = DATA_DIR / filename
|
||||||
|
if not file_path.exists():
|
||||||
|
raise FileNotFoundError(f"Fichier requis manquant : '{filename}' dans le dossier data/")
|
||||||
|
|
||||||
|
with open(file_path, "r", encoding="utf-8") as f:
|
||||||
|
try:
|
||||||
|
content = json.load(f)
|
||||||
|
except json.JSONDecodeError as e:
|
||||||
|
raise ValueError(f"Erreur de syntaxe JSON dans '{filename}' : {e}")
|
||||||
|
|
||||||
|
# Validation des clés requises
|
||||||
|
if isinstance(content, dict):
|
||||||
|
for key in required_keys:
|
||||||
|
if key not in content or not str(content[key]).strip():
|
||||||
|
raise KeyError(f"Clé manquante ou vide '{key}' dans '{filename}'")
|
||||||
|
|
||||||
|
key_name = filename.replace(".json", "")
|
||||||
|
data[key_name] = content
|
||||||
|
|
||||||
|
return data
|
||||||
@@ -0,0 +1,30 @@
|
|||||||
|
from datetime import datetime
|
||||||
|
from pathlib import Path
|
||||||
|
from loader import load_json_data
|
||||||
|
from pdf_builder import generate_pdf
|
||||||
|
|
||||||
|
def main():
|
||||||
|
# 1. Chargement et validation des données JSON
|
||||||
|
try:
|
||||||
|
data = load_json_data()
|
||||||
|
except Exception as e:
|
||||||
|
print(f"❌ Erreur lors du chargement des données : {e}")
|
||||||
|
return
|
||||||
|
|
||||||
|
# 2. Préparation du dossier de sortie
|
||||||
|
export_dir = Path(__file__).parent / "exports"
|
||||||
|
export_dir.mkdir(exist_ok=True)
|
||||||
|
|
||||||
|
# 3. Formatage du nom de fichier
|
||||||
|
timestamp = datetime.now().strftime("%Y-%m-%d_%H-%M-%S")
|
||||||
|
title_raw = data.get("context", {}).get("title", "CV")
|
||||||
|
title_clean = "".join(c if c.isalnum() else "_" for c in title_raw)
|
||||||
|
|
||||||
|
filename = f"{timestamp}_{title_clean}.pdf"
|
||||||
|
output_path = export_dir / filename
|
||||||
|
|
||||||
|
# 4. Génération du PDF
|
||||||
|
generate_pdf(data, str(output_path))
|
||||||
|
|
||||||
|
if __name__ == "__main__":
|
||||||
|
main()
|
||||||
+160
@@ -0,0 +1,160 @@
|
|||||||
|
import sys
|
||||||
|
from reportlab.lib.pagesizes import A4
|
||||||
|
from reportlab.lib.colors import HexColor
|
||||||
|
from reportlab.platypus import SimpleDocTemplate, Paragraph, Spacer, HRFlowable
|
||||||
|
from reportlab.lib.styles import getSampleStyleSheet, ParagraphStyle
|
||||||
|
from config import (
|
||||||
|
USABLE_WIDTH, USABLE_HEIGHT, MARGIN_X_PT, MARGIN_Y_PT,
|
||||||
|
MINIMUM_POLICE_SIZE, COLOR_PRIMARY, COLOR_SECONDARY, COLOR_TEXT
|
||||||
|
)
|
||||||
|
|
||||||
|
def create_styles(scale: float):
|
||||||
|
"""Génère les styles dynamiques appliqués au texte selon le facteur d'échelle."""
|
||||||
|
styles = getSampleStyleSheet()
|
||||||
|
|
||||||
|
title_size = max(14 * scale, 9)
|
||||||
|
heading_size = max(11 * scale, 8)
|
||||||
|
body_size = 9 * scale
|
||||||
|
leading_ratio = 1.25
|
||||||
|
|
||||||
|
return {
|
||||||
|
"Name": ParagraphStyle(
|
||||||
|
"NameStyle",
|
||||||
|
fontName="Helvetica-Bold",
|
||||||
|
fontSize=title_size * 1.3,
|
||||||
|
leading=title_size * 1.3 * leading_ratio,
|
||||||
|
textColor=HexColor(COLOR_PRIMARY),
|
||||||
|
spaceAfter=2 * scale
|
||||||
|
),
|
||||||
|
"JobTitle": ParagraphStyle(
|
||||||
|
"JobTitleStyle",
|
||||||
|
fontName="Helvetica-Bold",
|
||||||
|
fontSize=title_size,
|
||||||
|
leading=title_size * leading_ratio,
|
||||||
|
textColor=HexColor(COLOR_SECONDARY),
|
||||||
|
spaceAfter=4 * scale
|
||||||
|
),
|
||||||
|
"SectionHeading": ParagraphStyle(
|
||||||
|
"SectionHeadingStyle",
|
||||||
|
fontName="Helvetica-Bold",
|
||||||
|
fontSize=heading_size,
|
||||||
|
leading=heading_size * leading_ratio,
|
||||||
|
textColor=HexColor(COLOR_PRIMARY),
|
||||||
|
spaceBefore=6 * scale,
|
||||||
|
spaceAfter=2 * scale
|
||||||
|
),
|
||||||
|
"Body": ParagraphStyle(
|
||||||
|
"BodyStyle",
|
||||||
|
fontName="Helvetica",
|
||||||
|
fontSize=body_size,
|
||||||
|
leading=body_size * leading_ratio,
|
||||||
|
textColor=HexColor(COLOR_TEXT),
|
||||||
|
spaceAfter=2 * scale
|
||||||
|
),
|
||||||
|
"SubText": ParagraphStyle(
|
||||||
|
"SubTextStyle",
|
||||||
|
fontName="Helvetica-Oblique",
|
||||||
|
fontSize=max(body_size * 0.9, 6),
|
||||||
|
leading=max(body_size * 0.9, 6) * leading_ratio,
|
||||||
|
textColor=HexColor(COLOR_TEXT)
|
||||||
|
),
|
||||||
|
"body_font_size": body_size
|
||||||
|
}
|
||||||
|
|
||||||
|
def build_story(data: dict, styles: dict, scale: float):
|
||||||
|
"""Construit la liste d'éléments Flowable du CV."""
|
||||||
|
story = []
|
||||||
|
|
||||||
|
# 1. En-tête : Identité
|
||||||
|
id_data = data["identity"]
|
||||||
|
context = data["context"]
|
||||||
|
links = data["hyperlinks"]
|
||||||
|
|
||||||
|
full_name = f"{id_data.get('first_name', '')} {id_data.get('last_name', '')}"
|
||||||
|
story.append(Paragraph(full_name, styles["Name"]))
|
||||||
|
story.append(Paragraph(context.get("title", ""), styles["JobTitle"]))
|
||||||
|
|
||||||
|
# Infos contact & liens
|
||||||
|
contact_bits = [id_data.get("email"), id_data.get("phone"), id_data.get("location")]
|
||||||
|
for k, v in links.items():
|
||||||
|
contact_bits.append(f'<a href="{v}"><u>{k}</u></a>')
|
||||||
|
|
||||||
|
contact_line = " • ".join(filter(None, contact_bits))
|
||||||
|
story.append(Paragraph(contact_line, styles["SubText"]))
|
||||||
|
story.append(Spacer(1, 4 * scale))
|
||||||
|
story.append(HRFlowable(width="100%", thickness=1, color=HexColor(COLOR_SECONDARY), spaceAfter=6 * scale))
|
||||||
|
|
||||||
|
# 2. Résumé
|
||||||
|
if context.get("summary"):
|
||||||
|
story.append(Paragraph("PROFIL", styles["SectionHeading"]))
|
||||||
|
story.append(Paragraph(context["summary"], styles["Body"]))
|
||||||
|
|
||||||
|
# 3. Expériences
|
||||||
|
if data.get("experiences"):
|
||||||
|
story.append(Paragraph("EXPÉRIENCES PROFESSIONNELLES", styles["SectionHeading"]))
|
||||||
|
for exp in data["experiences"]:
|
||||||
|
header = f"<b>{exp.get('role', '')}</b> — {exp.get('company', '')} ({exp.get('dates', '')})"
|
||||||
|
story.append(Paragraph(header, styles["Body"]))
|
||||||
|
for detail in exp.get("details", []):
|
||||||
|
bullet = f"• {detail}"
|
||||||
|
story.append(Paragraph(bullet, styles["Body"]))
|
||||||
|
|
||||||
|
# 4. Langues & Hobbies
|
||||||
|
if data.get("languages"):
|
||||||
|
story.append(Paragraph("LANGUES", styles["SectionHeading"]))
|
||||||
|
langs = [f"<b>{k}:</b> {v}" for k, v in data["languages"].items()]
|
||||||
|
story.append(Paragraph(" • ".join(langs), styles["Body"]))
|
||||||
|
|
||||||
|
if data.get("hobbies"):
|
||||||
|
story.append(Paragraph("CENTRES D'INTÉRÊT", styles["SectionHeading"]))
|
||||||
|
story.append(Paragraph(" • ".join(data["hobbies"]), styles["Body"]))
|
||||||
|
|
||||||
|
return story
|
||||||
|
|
||||||
|
def measure_story_height(story: list) -> float:
|
||||||
|
"""Calcule la hauteur totale cumulée du contenu."""
|
||||||
|
total_height = 0
|
||||||
|
for item in story:
|
||||||
|
_, h = item.wrap(USABLE_WIDTH, USABLE_HEIGHT)
|
||||||
|
total_height += h
|
||||||
|
return total_height
|
||||||
|
|
||||||
|
def generate_pdf(data: dict, output_path: str):
|
||||||
|
"""Calcule l'échelle optimale et génère le PDF."""
|
||||||
|
scale = 1.0
|
||||||
|
min_scale = 0.4
|
||||||
|
step = 0.03
|
||||||
|
|
||||||
|
# Ajustement dynamique jusqu'à ce que le contenu rentre sur 1 page
|
||||||
|
while scale >= min_scale:
|
||||||
|
styles = create_styles(scale)
|
||||||
|
story = build_story(data, styles, scale)
|
||||||
|
height = measure_story_height(story)
|
||||||
|
|
||||||
|
if height <= USABLE_HEIGHT:
|
||||||
|
break
|
||||||
|
scale -= step
|
||||||
|
|
||||||
|
critical_font_size = styles["body_font_size"]
|
||||||
|
|
||||||
|
# Alerte console si la police est sous le seuil critique
|
||||||
|
if critical_font_size < MINIMUM_POLICE_SIZE:
|
||||||
|
print(f"\n⚠️ [ALERTE TAILLE POLICE]")
|
||||||
|
print(f"La taille de police nécessaire ({critical_font_size:.1f}pt) est inférieure "
|
||||||
|
f"au seuil configuré ({MINIMUM_POLICE_SIZE}pt).")
|
||||||
|
choice = input("Voulez-vous quand même générer le fichier ? (o/N) : ").strip().lower()
|
||||||
|
if choice not in ['o', 'oui']:
|
||||||
|
print("Génération annulée.")
|
||||||
|
sys.exit(0)
|
||||||
|
|
||||||
|
# Document final
|
||||||
|
doc = SimpleDocTemplate(
|
||||||
|
output_path,
|
||||||
|
pagesize=A4,
|
||||||
|
leftMargin=MARGIN_X_PT,
|
||||||
|
rightMargin=MARGIN_X_PT,
|
||||||
|
topMargin=MARGIN_Y_PT,
|
||||||
|
bottomMargin=MARGIN_Y_PT
|
||||||
|
)
|
||||||
|
doc.build(story)
|
||||||
|
print(f"✅ CV généré avec succès : {output_path}")
|
||||||
@@ -0,0 +1,10 @@
|
|||||||
|
[project]
|
||||||
|
name = "cv-generator"
|
||||||
|
version = "0.1.0"
|
||||||
|
description = "Add your description here"
|
||||||
|
readme = "README.md"
|
||||||
|
requires-python = ">=3.14"
|
||||||
|
dependencies = [
|
||||||
|
"dotenv>=0.9.9",
|
||||||
|
"reportlab>=5.0.0",
|
||||||
|
]
|
||||||
@@ -0,0 +1,136 @@
|
|||||||
|
version = 1
|
||||||
|
revision = 3
|
||||||
|
requires-python = ">=3.14"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "charset-normalizer"
|
||||||
|
version = "3.4.9"
|
||||||
|
source = { registry = "https://pypi.org/simple" }
|
||||||
|
sdist = { url = "https://files.pythonhosted.org/packages/bd/2a/23f34ec9d04624958e137efdc394888716353190e75f25dd22c7a2c7a8aa/charset_normalizer-3.4.9.tar.gz", hash = "sha256:673611bbd43f0810bec0b0f028ddeaaa501190339cac411f347ac76917c3ae7b", size = 152439, upload-time = "2026-07-07T14:34:58.454Z" }
|
||||||
|
wheels = [
|
||||||
|
{ url = "https://files.pythonhosted.org/packages/7e/8d/496817fa0944239ecae662dd57ea765cfeaec6a735f9f025d4b7b72e7143/charset_normalizer-3.4.9-cp314-cp314-macosx_10_15_universal2.whl", hash = "sha256:0327fcd59a935777d83410750c50600ee9571af2846f71ce40f25b13da1ef380", size = 317253, upload-time = "2026-07-07T14:33:54.994Z" },
|
||||||
|
{ url = "https://files.pythonhosted.org/packages/2b/f9/ef4a69ea338ad3c0deceea0f5f7d2380ae8b52132b06d652cb0d2cd86706/charset_normalizer-3.4.9-cp314-cp314-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:8a79d9f4d8001473a30c163556b3c3bfebec837495a412dde78b51672f6134f9", size = 215898, upload-time = "2026-07-07T14:33:56.334Z" },
|
||||||
|
{ url = "https://files.pythonhosted.org/packages/8c/e7/5ddfd76fc061eb52de219658a4aa431cbacadf0a0219c8854f00da50d289/charset_normalizer-3.4.9-cp314-cp314-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:33bdcc2a32c0a0e861f60841a512c8acc658c87c2ac59d89e3a46dacf7d866e4", size = 236718, upload-time = "2026-07-07T14:33:57.9Z" },
|
||||||
|
{ url = "https://files.pythonhosted.org/packages/49/ba/768fa3f36048d81c477a0ce61f813bc1454d80917ccfe550abd9f44f5e24/charset_normalizer-3.4.9-cp314-cp314-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:f840ed6d8ecba8255df8c42b87fadeda98ddfc6eeec05e2dc66e26d46dd6f58a", size = 232519, upload-time = "2026-07-07T14:33:59.811Z" },
|
||||||
|
{ url = "https://files.pythonhosted.org/packages/f4/c4/b3e049d2aa3766180c78507110543d9d50894cc97f57de543f1be521dcdc/charset_normalizer-3.4.9-cp314-cp314-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:c25fe15c70c59eb7c5ce8c06a1f3fa1da0ecc5ea1e7a5922c40fd2fa9b0d5046", size = 223143, upload-time = "2026-07-07T14:34:01.517Z" },
|
||||||
|
{ url = "https://files.pythonhosted.org/packages/19/79/55c32d06d76ae4feafe053f061f3e3ab70bcf19f4007797ce8c3efda7830/charset_normalizer-3.4.9-cp314-cp314-manylinux_2_31_armv7l.whl", hash = "sha256:f7fb7d750cfa0a070d2c24e831fd3481019a60dd317ea2b39acbcebc08b6ed81", size = 206742, upload-time = "2026-07-07T14:34:03.04Z" },
|
||||||
|
{ url = "https://files.pythonhosted.org/packages/10/e0/47c079dd82d217c807479cd59ffd30af56307ea31c108b75758970459ad3/charset_normalizer-3.4.9-cp314-cp314-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:4d1c96a7a18b9690a4d46df09e3e3382406ae3213727cd1019ebade1c4a81917", size = 219191, upload-time = "2026-07-07T14:34:04.657Z" },
|
||||||
|
{ url = "https://files.pythonhosted.org/packages/42/ab/b9bc2e77d6b44a7e46ef62ec5cac1c9a6ba7b9135a5d560f002696ec9995/charset_normalizer-3.4.9-cp314-cp314-musllinux_1_2_aarch64.whl", hash = "sha256:a4cfde78a9f2880208d16a93b795726a3017d5977e08d1e162a7a31322479c41", size = 218328, upload-time = "2026-07-07T14:34:06.115Z" },
|
||||||
|
{ url = "https://files.pythonhosted.org/packages/f1/78/c9c71d599f5aa2d42bcdd35cbbd46d7f535351a57e40ff7d8e5a7e219401/charset_normalizer-3.4.9-cp314-cp314-musllinux_1_2_armv7l.whl", hash = "sha256:d4d6fcde76f94f5cb9e43e9e9a61f16dacefd228cbbf6f1a09bd9b219a92f1a1", size = 207406, upload-time = "2026-07-07T14:34:07.554Z" },
|
||||||
|
{ url = "https://files.pythonhosted.org/packages/f6/39/c914445c321a845097ce4f6ac7de9a18228a77b766272125a1ce00d851eb/charset_normalizer-3.4.9-cp314-cp314-musllinux_1_2_x86_64.whl", hash = "sha256:898f0e9068ca27d37f8e83a5b962821df851532e6c4a7d615c1c033f9da6eedf", size = 225157, upload-time = "2026-07-07T14:34:09.061Z" },
|
||||||
|
{ url = "https://files.pythonhosted.org/packages/9b/f2/c0d4b8508565a36bc5c624e88ed297f5b0b1095011034d7f5b83a69908b5/charset_normalizer-3.4.9-cp314-cp314-win32.whl", hash = "sha256:c1c948747b03be832dceed96ca815cef7360de9aa19d37c730f8e3f6101aca48", size = 151095, upload-time = "2026-07-07T14:34:10.901Z" },
|
||||||
|
{ url = "https://files.pythonhosted.org/packages/49/fd/a1d26144398c67486422a72bf5812cda22cb4ccfcd95a290fb41ceb4b8e2/charset_normalizer-3.4.9-cp314-cp314-win_amd64.whl", hash = "sha256:16b65ea0f2465b6fb52aa22de5eca612aa964ddfec00a912e26f4656cbef890b", size = 162796, upload-time = "2026-07-07T14:34:12.47Z" },
|
||||||
|
{ url = "https://files.pythonhosted.org/packages/20/95/d75e82f8ce9fd323ebf059c16c9aadefb22a1ecde13b7840b35835e4886c/charset_normalizer-3.4.9-cp314-cp314-win_arm64.whl", hash = "sha256:40a126142a56b2dfc0aacbad1de8310cbf60da7656db0e6b16eebd48e3e93519", size = 153334, upload-time = "2026-07-07T14:34:14.044Z" },
|
||||||
|
{ url = "https://files.pythonhosted.org/packages/00/5e/17398df3a139985ba9d11ed072531986f408c8fca952835ef1ab1820c02b/charset_normalizer-3.4.9-cp314-cp314t-macosx_10_15_universal2.whl", hash = "sha256:609b3ba8fcc0fb5ab7af00719d0fb6ad0cb518e48e7712d12fd68f1327951198", size = 338848, upload-time = "2026-07-07T14:34:15.688Z" },
|
||||||
|
{ url = "https://files.pythonhosted.org/packages/cd/91/7253a32e86b7e1d1239b1b36ba6dd0f021a21107ab33054b53119cc083b9/charset_normalizer-3.4.9-cp314-cp314t-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:51447e9aa2684679af07ca5021c3db526e0284347ebf4ffcec1154c3350cfe32", size = 223022, upload-time = "2026-07-07T14:34:17.248Z" },
|
||||||
|
{ url = "https://files.pythonhosted.org/packages/cb/32/2e64bd2be10e89c61e57ebe6a93fd98ae88eb7ebe414b5121f22c96c69eb/charset_normalizer-3.4.9-cp314-cp314t-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:cc1b0fff8ead343dae06305f954eb8468ba0ec1a97881f42489d198e4ce3c632", size = 241590, upload-time = "2026-07-07T14:34:18.813Z" },
|
||||||
|
{ url = "https://files.pythonhosted.org/packages/3d/ef/d96ec496cfea0c21db43b0ad03891308b02388d054cc902cf0e5a1ad6a88/charset_normalizer-3.4.9-cp314-cp314t-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:fa36ec09ef71d158186bc79e359ff5fdd6e7996fe8ab638f00d6b93139ba4fcf", size = 239584, upload-time = "2026-07-07T14:34:20.52Z" },
|
||||||
|
{ url = "https://files.pythonhosted.org/packages/d4/ce/9af95f7876194bd7a14e3dfe4a4de2e0bff02666a3910d72beafd06cc297/charset_normalizer-3.4.9-cp314-cp314t-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:df115d4d83168fdf2cae48ef1ff6d1cb4c466364e30861b37121de0f3bf1b990", size = 230224, upload-time = "2026-07-07T14:34:22.189Z" },
|
||||||
|
{ url = "https://files.pythonhosted.org/packages/52/94/af74dde74a3996bd959c350709bfe50e297823d70a8c1cbd54b838880863/charset_normalizer-3.4.9-cp314-cp314t-manylinux_2_31_armv7l.whl", hash = "sha256:f86c6358749bd4fda175388691e3ba8c46e24c5347d0afd20f9b7edfc9faf07d", size = 212667, upload-time = "2026-07-07T14:34:23.857Z" },
|
||||||
|
{ url = "https://files.pythonhosted.org/packages/ee/f0/f1c4fe746c395922961b5916ed1d7d6e7d4c84851d19ed43cc89980ec953/charset_normalizer-3.4.9-cp314-cp314t-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:32286a2c8d167e897177b673176c1e3e00d4057caf5d2b64eef9a3666b03018e", size = 227179, upload-time = "2026-07-07T14:34:25.586Z" },
|
||||||
|
{ url = "https://files.pythonhosted.org/packages/e4/56/6c745619ac397e8871e2bcd3cea1eec86b877488f33888b3aef5c3ed506e/charset_normalizer-3.4.9-cp314-cp314t-musllinux_1_2_aarch64.whl", hash = "sha256:83aed2c10721ddd90f68140685391b50811a880af20654c59af6b6c66c40513c", size = 225372, upload-time = "2026-07-07T14:34:27.212Z" },
|
||||||
|
{ url = "https://files.pythonhosted.org/packages/78/ad/98aae8630ac71f16711968e38a5acfecce41b778bf2f0312851020f565a8/charset_normalizer-3.4.9-cp314-cp314t-musllinux_1_2_armv7l.whl", hash = "sha256:cd6c3d4b783c556fa00bf540854e42f135e2f256abd29669fcd0da0f2dec79c2", size = 215222, upload-time = "2026-07-07T14:34:28.774Z" },
|
||||||
|
{ url = "https://files.pythonhosted.org/packages/f7/40/9593d54209765207a7f11073c06494c1721e4ca4a0a426c597679bf7f91e/charset_normalizer-3.4.9-cp314-cp314t-musllinux_1_2_x86_64.whl", hash = "sha256:ee2f2a527e3c1a6e6411eb4209642e138b544a2d72fe5d0d76daf77b24063534", size = 231958, upload-time = "2026-07-07T14:34:30.345Z" },
|
||||||
|
{ url = "https://files.pythonhosted.org/packages/b1/27/693ee5e8a18191eb38647360c51cd505013e2bd3b366aa43fd5344c21e3c/charset_normalizer-3.4.9-cp314-cp314t-win32.whl", hash = "sha256:0d861473f743244d349b50f850d10eb87aeb22bbdcc8e64f79273c94af5a8226", size = 155580, upload-time = "2026-07-07T14:34:31.884Z" },
|
||||||
|
{ url = "https://files.pythonhosted.org/packages/80/3f/bd97d3d9c613013d07cb7733d299385b41df37f0471310f5a73dc359f0b8/charset_normalizer-3.4.9-cp314-cp314t-win_amd64.whl", hash = "sha256:9b8e0f3107e2200b76f6054de99016eac3ee6762713587b36baaa7e4bd2ae177", size = 167620, upload-time = "2026-07-07T14:34:33.438Z" },
|
||||||
|
{ url = "https://files.pythonhosted.org/packages/3d/c6/eee9dca4439b1061f76373f06ea855678cc4a64c1c3c90b50e479edbb8eb/charset_normalizer-3.4.9-cp314-cp314t-win_arm64.whl", hash = "sha256:19ac87f93086ce37b86e098888555c4b4bc48102279bae3350098c0ed664b501", size = 158037, upload-time = "2026-07-07T14:34:35.018Z" },
|
||||||
|
{ url = "https://files.pythonhosted.org/packages/98/2b/f97f1c193fb855c345d678f5077d6926034db0722df74c8f057020e05a25/charset_normalizer-3.4.9-py3-none-any.whl", hash = "sha256:68e5f26a1ad57ded6d1cfb85331d1c1a195314756471d97758c48498bb4dcdf5", size = 64538, upload-time = "2026-07-07T14:34:56.993Z" },
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "cv-generator"
|
||||||
|
version = "0.1.0"
|
||||||
|
source = { virtual = "." }
|
||||||
|
dependencies = [
|
||||||
|
{ name = "dotenv" },
|
||||||
|
{ name = "reportlab" },
|
||||||
|
]
|
||||||
|
|
||||||
|
[package.metadata]
|
||||||
|
requires-dist = [
|
||||||
|
{ name = "dotenv", specifier = ">=0.9.9" },
|
||||||
|
{ name = "reportlab", specifier = ">=5.0.0" },
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "dotenv"
|
||||||
|
version = "0.9.9"
|
||||||
|
source = { registry = "https://pypi.org/simple" }
|
||||||
|
dependencies = [
|
||||||
|
{ name = "python-dotenv" },
|
||||||
|
]
|
||||||
|
wheels = [
|
||||||
|
{ url = "https://files.pythonhosted.org/packages/b2/b7/545d2c10c1fc15e48653c91efde329a790f2eecfbbf2bd16003b5db2bab0/dotenv-0.9.9-py2.py3-none-any.whl", hash = "sha256:29cf74a087b31dafdb5a446b6d7e11cbce8ed2741540e2339c69fbef92c94ce9", size = 1892, upload-time = "2025-02-19T22:15:01.647Z" },
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "pillow"
|
||||||
|
version = "12.3.0"
|
||||||
|
source = { registry = "https://pypi.org/simple" }
|
||||||
|
sdist = { url = "https://files.pythonhosted.org/packages/1c/3d/bb7fca845737cf9d7dbde16ed1843984665ff2e0a518f5db43e77ec540b9/pillow-12.3.0.tar.gz", hash = "sha256:3b8182a766685eaa002637e28b4ec8d6b18819a0c71f579bf0dbaa5830297cce", size = 47025035, upload-time = "2026-07-01T11:56:38.965Z" }
|
||||||
|
wheels = [
|
||||||
|
{ url = "https://files.pythonhosted.org/packages/dc/01/001f65b68192f0228cc1dbbc8d2530ab5d58b61037ba0587f946fea607cd/pillow-12.3.0-cp314-cp314-ios_13_0_arm64_iphoneos.whl", hash = "sha256:9cf95fe4d0f84c82d282745d9bb08ad9f926efa00be4697e767b814ce40d4330", size = 4161736, upload-time = "2026-07-01T11:54:51.156Z" },
|
||||||
|
{ url = "https://files.pythonhosted.org/packages/1a/d2/0219746d0fd16fc8a84498e79452375be3797d3ce4044596ce565164b84f/pillow-12.3.0-cp314-cp314-ios_13_0_arm64_iphonesimulator.whl", hash = "sha256:8728f216dcdb6e6d555cf971cb34076139ad74b31fc2c14da4fafc741c5f6217", size = 4255435, upload-time = "2026-07-01T11:54:53.414Z" },
|
||||||
|
{ url = "https://files.pythonhosted.org/packages/c8/02/8d0bc62ef0302318c46ff2a512822d2610e81c7aa46c9b3abe6cbaca5ad0/pillow-12.3.0-cp314-cp314-ios_13_0_x86_64_iphonesimulator.whl", hash = "sha256:a45650e8ce7fafffd731db8550230db6b0d306d181a90b67d3e6bca2f1990930", size = 3696262, upload-time = "2026-07-01T11:54:55.739Z" },
|
||||||
|
{ url = "https://files.pythonhosted.org/packages/85/e2/73c77d218410b14f5f2d565e8a998d5317b7b9c75368d29985139f7a46f0/pillow-12.3.0-cp314-cp314-macosx_10_15_x86_64.whl", hash = "sha256:ba54cfebe86920a559a7c4d6b9050791c20513650a1952ebe3368c7dc70306f8", size = 5350344, upload-time = "2026-07-01T11:54:57.657Z" },
|
||||||
|
{ url = "https://files.pythonhosted.org/packages/c7/da/32c752228ae345f489e3a42499d817b6c3996da7e8a3bc7a04fc806b243b/pillow-12.3.0-cp314-cp314-macosx_11_0_arm64.whl", hash = "sha256:e158cb00350dc278f3b91551101aa7d12415a66ebf2c91d8d5ac14e56ddd3ad0", size = 4780131, upload-time = "2026-07-01T11:54:59.713Z" },
|
||||||
|
{ url = "https://files.pythonhosted.org/packages/b1/9d/8b2c807dbef61a5197c047afe99823787eb66f63daf9fb2432f91d6f0462/pillow-12.3.0-cp314-cp314-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:e9aeb04d6aef139de265b29683e119b638208f88cf73cdd1658aa07221165321", size = 6263757, upload-time = "2026-07-01T11:55:01.778Z" },
|
||||||
|
{ url = "https://files.pythonhosted.org/packages/5c/44/c85361f65dbe00eea8576ee467c768d25129989efb76e94f205e9ca9bb46/pillow-12.3.0-cp314-cp314-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:251bf95b67017e27b13d82f5b326234ca62d70f9cf4c2b9032de2358a3b12c7b", size = 6936962, upload-time = "2026-07-01T11:55:03.93Z" },
|
||||||
|
{ url = "https://files.pythonhosted.org/packages/18/7e/e483414b35800b86b6f08dbbc7803fb5cd52c4d6f897f47d53ea2c7e6f65/pillow-12.3.0-cp314-cp314-musllinux_1_2_aarch64.whl", hash = "sha256:fe3cca2e4e8a592be0f269a1ca4835c25199d9f3ce815c8491048f785b0a0198", size = 6339171, upload-time = "2026-07-01T11:55:05.989Z" },
|
||||||
|
{ url = "https://files.pythonhosted.org/packages/f0/f4/68c491844841ede6bed70189546b3ee9731cf9f2cbad396faff5e1ccba45/pillow-12.3.0-cp314-cp314-musllinux_1_2_x86_64.whl", hash = "sha256:23aceaa007d6172b02c277f0cd359c79492bbb14f7072b4ede9fbcaf20648130", size = 7048116, upload-time = "2026-07-01T11:55:08.131Z" },
|
||||||
|
{ url = "https://files.pythonhosted.org/packages/a3/34/77f3f793fed8efc7d243f21b33c5a3f0d1c97ee70346d3db855587e155ff/pillow-12.3.0-cp314-cp314-win32.whl", hash = "sha256:af8d94b0db561cf68b88a267c5c44b49e134f525d0dc2cb7ed413a66bc23559a", size = 6467209, upload-time = "2026-07-01T11:55:10.408Z" },
|
||||||
|
{ url = "https://files.pythonhosted.org/packages/f1/e0/492879f69d94f91f60fc8cd05ba03650e9520afebb2fb7aa12777d7c7f38/pillow-12.3.0-cp314-cp314-win_amd64.whl", hash = "sha256:fdafc9cce40277e0f7a0feabce0ee50dd2fa1800f3b38015e51296b5e814048d", size = 7237707, upload-time = "2026-07-01T11:55:12.745Z" },
|
||||||
|
{ url = "https://files.pythonhosted.org/packages/c9/ac/6b11f2875f1c2ac040d84e1bbf9cf22a88038f901ca1037898b280b38365/pillow-12.3.0-cp314-cp314-win_arm64.whl", hash = "sha256:e91206ee562682b51b98ef4b26a6ef48fd84e15fd4c4bc5ec768eb641d206838", size = 2565995, upload-time = "2026-07-01T11:55:14.736Z" },
|
||||||
|
{ url = "https://files.pythonhosted.org/packages/52/69/c2208e56af9bfc1913afb24020297a691eb1d4ef688474c8a04913f65e04/pillow-12.3.0-cp314-cp314t-macosx_10_15_x86_64.whl", hash = "sha256:164b31cd1a0490ab6efae01aa5df49da7061be0af1b30e035b6e9a1bfe34ee6e", size = 5352503, upload-time = "2026-07-01T11:55:17.076Z" },
|
||||||
|
{ url = "https://files.pythonhosted.org/packages/07/70/e5686d753e898a45d778ff1718dba8516ead6ab6b95d85fc8c4b70650cf2/pillow-12.3.0-cp314-cp314t-macosx_11_0_arm64.whl", hash = "sha256:5afb51d599ea772b8365ae807ae557f18bccfe46ab261fd1c2a9ed700fc6eb17", size = 4782956, upload-time = "2026-07-01T11:55:19.448Z" },
|
||||||
|
{ url = "https://files.pythonhosted.org/packages/d5/37/25c6692f06927ee973ff18c8d9ee98ad0b4d84ee67a09610c2dd1447958e/pillow-12.3.0-cp314-cp314t-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:3edce1d53195db527e0191f84b71d02022de0540bf43a16ed734ed7537b07385", size = 6322855, upload-time = "2026-07-01T11:55:21.613Z" },
|
||||||
|
{ url = "https://files.pythonhosted.org/packages/cc/91/420637fcb8f1bc11029e403b4538e6694744428d8246118e45719f944556/pillow-12.3.0-cp314-cp314t-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:bf16ba1b4d0b6b7c8e534936632270cf70eb00dbe09005bc345b2677b726855c", size = 6989642, upload-time = "2026-07-01T11:55:24.006Z" },
|
||||||
|
{ url = "https://files.pythonhosted.org/packages/10/08/b94d7811281ccf0d143a1cf768d1c49e1e54af63e7b708ab2ee3eb87face/pillow-12.3.0-cp314-cp314t-musllinux_1_2_aarch64.whl", hash = "sha256:24870b09b224f7ae3c39ed07d10e819d06f8720bc551847b1d623832b5b0e28d", size = 6391281, upload-time = "2026-07-01T11:55:26.252Z" },
|
||||||
|
{ url = "https://files.pythonhosted.org/packages/d2/87/24233f785f55474dc02ce3e739c5528a77e3a862e9333d1dd7a25cc31f70/pillow-12.3.0-cp314-cp314t-musllinux_1_2_x86_64.whl", hash = "sha256:30f2aa603c41533cc25c05acd0da21636e84a315768feb631c937177db558931", size = 7096716, upload-time = "2026-07-01T11:55:28.318Z" },
|
||||||
|
{ url = "https://files.pythonhosted.org/packages/23/26/fcb2f6e37175b04f53570b59937867e2b80ee1685e744023153028fc14f9/pillow-12.3.0-cp314-cp314t-win32.whl", hash = "sha256:4b0a7fe987b14c31ebda6083f74f22b561fd3739bc0ac51e019622e3d72668c7", size = 6474125, upload-time = "2026-07-01T11:55:30.956Z" },
|
||||||
|
{ url = "https://files.pythonhosted.org/packages/90/de/3634abee5f1c9e13c56787b7d5517b0ba8d6de51700b95578cf338349c9f/pillow-12.3.0-cp314-cp314t-win_amd64.whl", hash = "sha256:962864dc93511324d51ddbb5b9f8731bf71675b93ca612a07441896f4688fb8c", size = 7242939, upload-time = "2026-07-01T11:55:34.044Z" },
|
||||||
|
{ url = "https://files.pythonhosted.org/packages/ce/2a/fd13f8eb24de5714a6eb444a3d67e2842c6c576e159a43793adf23051351/pillow-12.3.0-cp314-cp314t-win_arm64.whl", hash = "sha256:0740a512dc522224c77d9aa5a8d70d8b7d73fb91f2c21125d8d025d3b8990e45", size = 2567506, upload-time = "2026-07-01T11:55:35.988Z" },
|
||||||
|
{ url = "https://files.pythonhosted.org/packages/5d/dc/8fdce34ec725a33c81c6ba122b904d6b9024e50ea9ac7bede62fab54506c/pillow-12.3.0-cp315-cp315-ios_13_0_arm64_iphoneos.whl", hash = "sha256:0feb2e9d6ad6c9e3c06effe9d00f3f1e618a6643273576b016f591e9315a7139", size = 4162063, upload-time = "2026-07-01T11:55:37.941Z" },
|
||||||
|
{ url = "https://files.pythonhosted.org/packages/76/66/2044b9a63d3b84ff048228dfcb7cd9bf0df983e8470971bf7d4c57b693de/pillow-12.3.0-cp315-cp315-ios_13_0_arm64_iphonesimulator.whl", hash = "sha256:9e881fca225083806662a5c43d627d215f258ff43c890f831966c7d7ba9c7402", size = 4255549, upload-time = "2026-07-01T11:55:40.022Z" },
|
||||||
|
{ url = "https://files.pythonhosted.org/packages/52/7e/1f67e6f4ece6b582ee4b539decbcc9f848dc245a93ed8cd7338bafef72f1/pillow-12.3.0-cp315-cp315-ios_13_0_x86_64_iphonesimulator.whl", hash = "sha256:4998562bf62a445225f22e07c896bb04b35b1b1f2eb6d760584c9c51d7a5f78c", size = 3696331, upload-time = "2026-07-01T11:55:41.98Z" },
|
||||||
|
{ url = "https://files.pythonhosted.org/packages/12/40/d306fc2c8e4d45d7f175c77edca7063be7b86fe7fe6e68f4353bf71d808c/pillow-12.3.0-cp315-cp315-macosx_10_15_x86_64.whl", hash = "sha256:dc624f6bc473dacdf7ef7eb8678d0d08edf15cd94fad6ae5c7d6cc67a4e4902f", size = 5350370, upload-time = "2026-07-01T11:55:44.028Z" },
|
||||||
|
{ url = "https://files.pythonhosted.org/packages/dd/44/668fb1437e8ce420f62d6106eb66e44a5971602a4d794615bdf79315d82d/pillow-12.3.0-cp315-cp315-macosx_11_0_arm64.whl", hash = "sha256:71d6097b330eea8fd15097780c8e89cb1a8ce7838669f48c5bacd6f663dd4701", size = 4780147, upload-time = "2026-07-01T11:55:46.073Z" },
|
||||||
|
{ url = "https://files.pythonhosted.org/packages/0c/08/93fa2e70e30a2d81547e481b6ee2bb9522117221fb1e0ce4b5df70967677/pillow-12.3.0-cp315-cp315-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:28ce87c5ab450a9dd970b52e5aca5fe63ed432d18a2eaddd1979a00a1ba24ace", size = 6273659, upload-time = "2026-07-01T11:55:48.264Z" },
|
||||||
|
{ url = "https://files.pythonhosted.org/packages/f8/6d/043e96ff814fc31a33077e4cba86082167db520c93632afdf2042febbb0c/pillow-12.3.0-cp315-cp315-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:6b02afb9b97f65fbca5f31db6a2a3ba21aa93030225f150fa3f249717e938fb4", size = 6947439, upload-time = "2026-07-01T11:55:50.503Z" },
|
||||||
|
{ url = "https://files.pythonhosted.org/packages/af/92/ba71d2ee2ac0edf3fa33bd9d5ee9ee080da70b1766f3ca3934f9938ddac9/pillow-12.3.0-cp315-cp315-musllinux_1_2_aarch64.whl", hash = "sha256:1182d52bc2d5e5d7d0949503aa7e36d12f42205dc287e4883f407b1988820d39", size = 6353577, upload-time = "2026-07-01T11:55:52.697Z" },
|
||||||
|
{ url = "https://files.pythonhosted.org/packages/0f/ce/e63064e2122923ff687c8ad792d0d736a7b3920a56a46982e81a7fdd25d6/pillow-12.3.0-cp315-cp315-musllinux_1_2_x86_64.whl", hash = "sha256:e795b7eb908249c4e43c7c99fac7c2c75dab0c43566e37db472a355f63693d71", size = 7060394, upload-time = "2026-07-01T11:55:55.149Z" },
|
||||||
|
{ url = "https://files.pythonhosted.org/packages/54/76/a09cc3ccc8d773a7283d34c38bec1708f9e3cc932093cbc4c5e71ac4060b/pillow-12.3.0-cp315-cp315-win32.whl", hash = "sha256:57b3d78c95ba9059768b10e28b813002261d3f3dfc55cc48b0c988f625175827", size = 6467375, upload-time = "2026-07-01T11:55:57.769Z" },
|
||||||
|
{ url = "https://files.pythonhosted.org/packages/3e/03/1846c49ba3b1d5550392a4bbd06d6fb4578e1cd91a803198b5c90f5f7d53/pillow-12.3.0-cp315-cp315-win_amd64.whl", hash = "sha256:fa4ecea169a355be7a3ade2c783e2ed12f0e40d2c5621cda8b3297faf7fbb9f5", size = 7237048, upload-time = "2026-07-01T11:55:59.975Z" },
|
||||||
|
{ url = "https://files.pythonhosted.org/packages/fb/bb/89f35dcc79610423f9f195504d7def7f0d1416a711541b42867e25fe3412/pillow-12.3.0-cp315-cp315-win_arm64.whl", hash = "sha256:877c3f311ff35410f690861c4409e7ccbf0cd2f878e50628a28e5a0bb689e658", size = 2566006, upload-time = "2026-07-01T11:56:02.143Z" },
|
||||||
|
{ url = "https://files.pythonhosted.org/packages/30/88/707027ba09942dfa2c28759b5c222d769290a41c6d20ea60ec250801941f/pillow-12.3.0-cp315-cp315t-macosx_10_15_x86_64.whl", hash = "sha256:e9871b1ffbfa9656b60aeee92ed5136a5742696006fa322b29ea3d8da0ecc9cf", size = 5352509, upload-time = "2026-07-01T11:56:04.2Z" },
|
||||||
|
{ url = "https://files.pythonhosted.org/packages/b0/6d/00352fa25332c2569cd387851f568cc5a4b75a9adbfb37ac4fbce4c02eec/pillow-12.3.0-cp315-cp315t-macosx_11_0_arm64.whl", hash = "sha256:53aa02d20d10c3d814d536aa4e5ac9b84ca0ff5a88377963b085ad6822f93e64", size = 4783167, upload-time = "2026-07-01T11:56:06.631Z" },
|
||||||
|
{ url = "https://files.pythonhosted.org/packages/13/4f/9e049dfa21af7c22427275720e2490267ba8138120add5c4c574deb69782/pillow-12.3.0-cp315-cp315t-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:446c34dcc4324b084a53b705127dc15717b22c5e140ae0a3c38349d4efec071e", size = 6329237, upload-time = "2026-07-01T11:56:08.868Z" },
|
||||||
|
{ url = "https://files.pythonhosted.org/packages/36/16/cf6eeaae8d0fce8dd390a33437cf68c5d5bd73834a2bc6e2f14efda0ab45/pillow-12.3.0-cp315-cp315t-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:cf1845d02ad822a369a49f2bb9345b1614744267682e7a03527dc3bf6eea1777", size = 6997047, upload-time = "2026-07-01T11:56:11.379Z" },
|
||||||
|
{ url = "https://files.pythonhosted.org/packages/1e/69/dbf769bdd55f48bf5733cac28edc6364ffaa072ec9ba336266e4fe66be55/pillow-12.3.0-cp315-cp315t-musllinux_1_2_aarch64.whl", hash = "sha256:186941b6aef820ad110fb01fb06eb925374dc3a21b17e37ec9a53b250c6fe2d1", size = 6400440, upload-time = "2026-07-01T11:56:13.908Z" },
|
||||||
|
{ url = "https://files.pythonhosted.org/packages/a0/e1/ffc9cfc2eea0d178da8018e18e959301ad9d6bc9f3edb7181e748a474b97/pillow-12.3.0-cp315-cp315t-musllinux_1_2_x86_64.whl", hash = "sha256:f13c32a3abd6079a66d9526e18dad9b6d280384d49d7c54040cd57b6424041d9", size = 7105895, upload-time = "2026-07-01T11:56:16.575Z" },
|
||||||
|
{ url = "https://files.pythonhosted.org/packages/18/f0/a5595c1e8c3ae44b9828cb2f0fa8155e5095ef04d6327b8f61cf44a3df85/pillow-12.3.0-cp315-cp315t-win32.whl", hash = "sha256:1657923d2d45afb66526e5b933e5b3052e6bdea196c90d3abb2424e18c77dae8", size = 6474384, upload-time = "2026-07-01T11:56:18.855Z" },
|
||||||
|
{ url = "https://files.pythonhosted.org/packages/e4/04/62bcd9f844984c5938d3b05264a61d797a29d3e0812341a8204af70bbdee/pillow-12.3.0-cp315-cp315t-win_amd64.whl", hash = "sha256:8cd2f7bdda092d99c9fc2fb7391354f306d01443d22785d0cbfafa2e2c8bb418", size = 7243537, upload-time = "2026-07-01T11:56:21.214Z" },
|
||||||
|
{ url = "https://files.pythonhosted.org/packages/3d/68/1f3066acedf37673694a7141381d8f811ae97f30d34413d236abe7d489f1/pillow-12.3.0-cp315-cp315t-win_arm64.whl", hash = "sha256:06ff022112bc9cbf83b60f8e028d94ad87b60621706487e65f673de61610ab59", size = 2567491, upload-time = "2026-07-01T11:56:23.506Z" },
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "python-dotenv"
|
||||||
|
version = "1.2.2"
|
||||||
|
source = { registry = "https://pypi.org/simple" }
|
||||||
|
sdist = { url = "https://files.pythonhosted.org/packages/82/ed/0301aeeac3e5353ef3d94b6ec08bbcabd04a72018415dcb29e588514bba8/python_dotenv-1.2.2.tar.gz", hash = "sha256:2c371a91fbd7ba082c2c1dc1f8bf89ca22564a087c2c287cd9b662adde799cf3", size = 50135, upload-time = "2026-03-01T16:00:26.196Z" }
|
||||||
|
wheels = [
|
||||||
|
{ url = "https://files.pythonhosted.org/packages/0b/d7/1959b9648791274998a9c3526f6d0ec8fd2233e4d4acce81bbae76b44b2a/python_dotenv-1.2.2-py3-none-any.whl", hash = "sha256:1d8214789a24de455a8b8bd8ae6fe3c6b69a5e3d64aa8a8e5d68e694bbcb285a", size = 22101, upload-time = "2026-03-01T16:00:25.09Z" },
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "reportlab"
|
||||||
|
version = "5.0.0"
|
||||||
|
source = { registry = "https://pypi.org/simple" }
|
||||||
|
dependencies = [
|
||||||
|
{ name = "charset-normalizer" },
|
||||||
|
{ name = "pillow" },
|
||||||
|
]
|
||||||
|
sdist = { url = "https://files.pythonhosted.org/packages/41/d6/4b7b0cf56880eb96533e607967be6a939e344675601e033d113a0bfa1f4e/reportlab-5.0.0.tar.gz", hash = "sha256:e4494a0c6623ae213bb856fba523171b2b54a7bf629fda02d5e525a7b899a784", size = 3701928, upload-time = "2026-06-18T11:34:31.145Z" }
|
||||||
|
wheels = [
|
||||||
|
{ url = "https://files.pythonhosted.org/packages/a3/07/70085c17a369605f15e301d10ab902115019b1126c7253d964afc230c7d6/reportlab-5.0.0-py3-none-any.whl", hash = "sha256:9d5a3affa84919e1111ede580031266a570e93b1ce388219621347965ff1d93c", size = 1956710, upload-time = "2026-06-18T11:34:29.07Z" },
|
||||||
|
]
|
||||||
Reference in New Issue
Block a user