Kgshop commited on
Commit
38a3650
·
verified ·
1 Parent(s): 5506526

Update app.py

Browse files
Files changed (1) hide show
  1. app.py +135 -131
app.py CHANGED
@@ -77,45 +77,45 @@ Render the product with hyperrealistic lighting and shadows that accentuate its
77
  **CONTEXT:** This is a professional, high-end commercial photoshoot for a children's clothing catalog or brand campaign. The overall atmosphere must be bright, clean, and joyful."""
78
  },
79
  "flagship_styles": {
80
- "studio": "Impeccable studio photoshoot. Flawless, even lighting on a neutral cyclorama (light grey, beige). Ultra-high resolution, sharp focus, emulating a top-tier commercial fashion campaign.",
81
- "street": "Dynamic street style shot in a bustling metropolis (e.g., Tokyo, New York). Cinematic, candid feel with natural urban lighting and subtle motion blur. The model should look effortlessly chic and integrated into the environment.",
82
- "lookbook": "Minimalist lookbook aesthetic. Clean, textured background (e.g., concrete, colored paper). Soft, diffused light creating a sophisticated and modern mood. Focus is entirely on the garment's form and drape.",
83
- "minimalism": "Extreme architectural minimalism. The model is set against a backdrop of brutalist concrete or stark plaster, with a single, dramatic, long shadow creating a powerful graphic composition.",
84
- "selfie": "Hyperrealistic 'captured moment' selfie. Shot on a smartphone in a visually interesting location (e.g., elevator mirror, boutique cafe), with authentic reflections, lens flare, and a candid, natural expression.",
85
- "creative": "Avant-garde, conceptual photoshoot. Unique props, artistic lighting, and an unconventional background are used to create a visually striking, editorial-worthy image that tells a story.",
86
- "new_year": "Festive New Year's atmosphere. Soft bokeh from fairy lights, dynamic sparkler trails, set against a beautifully decorated tree or a magical snowy landscape. Evokes warmth and celebration.",
87
- "retro": "Authentic 35mm film photograph emulation. Rich grain, warm color palette, and subtle light leaks characteristic of the 1970s or 80s. Poses and environment reflect the era.",
88
- "boho": "Golden hour boho dreamscape. Shot in a field of wildflowers during sunset. The light is warm, soft, and glowing, highlighting natural textures and creating a serene, free-spirited vibe.",
89
- "gothic": "Moody, gothic romance. Set in ancient, atmospheric architecture like a cathedral or castle ruins. Low-key lighting, deep shadows, and a sense of mystery and drama.",
90
- "editorial": "High-fashion glossy magazine editorial. Bold, saturated colored background. Clever use of mirrors to create compelling reflections and fragmented views of the model and outfit.",
91
- "film_noir": "Cinematic black and white film noir. High contrast, dramatic 'chiaroscuro' lighting, with long shadows, and a sense of suspense. May incorporate atmospheric elements like rain or fog.",
92
- "cottagecore": "Idyllic cottagecore aesthetic. A cozy, rustic setting in a country house or lush garden. Natural light, organic textures, and a feeling of wholesome, romanticized rural life.",
93
- "royalcore": "Opulent royalcore aesthetic. Set in a lavish palace interior with ornate details, velvet curtains, and gilded furniture. The lighting is grand and dramatic, creating an air of aristocracy.",
94
- "solarpunk": "Optimistic solarpunk future. Sleek, futuristic architecture seamlessly integrated with lush greenery. Bright, clean light fills the scene, suggesting a harmonious, tech-advanced society.",
95
- "skater": "Energetic skater aesthetic. Wide-angle, dynamic shot in a skate park or on urban streets. Captures movement and a raw, youthful, counter-culture energy.",
96
- "baroque": "Dramatic Baroque painting style. Ornate, detailed setting with rich fabrics. Lighting is high-contrast and theatrical, reminiscent of a Caravaggio masterpiece, creating deep, intense colors.",
97
- "japandi": "Serene Japandi style. A fusion of Japanese minimalism and Scandinavian functionality. Clean lines, neutral tones, natural wood, and a focus on tranquility and uncluttered space.",
98
- "coastal": "Relaxed coastal grandmother style. Bright, airy setting by the sea. A palette of whites, beiges, and soft blues. Natural materials and a feeling of effortless seaside elegance.",
99
- "cyberpunk": "Gritty, neon-drenched cyberpunk cityscape. High-tech, futuristic elements, with reflections from neon signs on wet streets. A cool color palette and a sense of urban dystopia.",
100
- "fantasy": "Enchanting fantasy world. A magical forest, ancient ruins, or ethereal landscape. The lighting is mystical and otherworldly, creating a dreamlike, narrative-driven image.",
101
- "90s_grunge": "Raw 90s grunge aesthetic. Urban decay, abandoned locations, with a desaturated color palette. A feeling of angst, rebellion, and effortless, non-conformist style.",
102
- "techwear": "Sleek, functional Techwear style. Set against futuristic, urban architecture. The lighting is clean and sharp, highlighting the technical details, fabrics, and functionality of the garments.",
103
- "avant_garde": "Experimental avant-garde fashion. Abstract shapes, bold color clashes, and unconventional compositions. A highly artistic and conceptual approach that challenges traditional aesthetics.",
104
- "home_casual": "Cozy, authentic home setting. Soft, natural light streaming through a window. A relaxed, intimate atmosphere with books, plants, and comfortable furnishings.",
105
- "social_media_candid": "Candid, 'Instagrammable' moment. Shot in a trendy cafe or during a walk. Looks spontaneous and natural, as if capturing a real moment in time.",
106
- "backstage": "Hectic, atmospheric backstage of a fashion show. Racks of clothes, makeup stations, and focused energy. The lighting is functional but chaotic, creating a 'behind-the-scenes' narrative.",
107
- "road_trip": "Cinematic American road trip aesthetic. The model is near a vintage car against a vast, open landscape at sunset. A sense of freedom, adventure, and nostalgia.",
108
- "rainy_day": "Romantic, melancholic rainy day scene. Reflections on wet pavement, droplets on windows, and the soft, diffused light of an overcast sky. A cozy and introspective mood.",
109
- "night_flash": "Edgy, direct-flash night photography. High contrast, saturated colors, and sharp shadows. Creates a raw, spontaneous, 'paparazzi' or party-snapshot feel.",
110
- "golden_hour_picnic": "Idyllic golden hour picnic. Warm, glowing sunset light filters through trees. A beautifully styled picnic scene with a relaxed, romantic, and joyful atmosphere.",
111
- "beach": "Vibrant beach scene. Shot during the golden hour with soft, warm sunlight. The model is near the water with gentle waves and fine sand in the background, creating a relaxed and sunny atmosphere."
112
  },
113
  "object_styles": {
114
- "studio": "Professional product photography on a seamless, neutral background. Perfect, multi-point lighting that eliminates harsh shadows and reveals every detail of the product's texture and form.",
115
- "minimalism": "Minimalist composition on a textured surface like concrete, marble, or fine sand. A single, crisp, hard light source creates a graphic, artistic shadow, emphasizing the product's silhouette.",
116
- "nature": "The product is artfully placed in a complementary natural environment. E.g., on mossy rocks in a forest, beside a clear stream, or nestled among flowers. The lighting is natural and enhances the organic feel.",
117
- "luxe": "Luxury still life. The product is arranged on a rich, tactile surface like silk, velvet, or dark marble. The lighting is low-key and sophisticated, with soft highlights that suggest opulence and exclusivity.",
118
- "dark": "Moody and dramatic 'dark academia' style. The product is set against a dark, textured background. A single, directional light source carves the product out of the shadows, creating a mysterious and intense atmosphere."
119
  }
120
  }
121
  with open(PROMPTS_FILE, 'w', encoding='utf-8') as f:
@@ -358,19 +358,27 @@ ADMHOSTO_TEMPLATE = '''
358
  .empty-list-placeholder { text-align:center; padding: 20px; color: #888; }
359
  .no-margin { margin-bottom: 0; }
360
 
361
- .summary-widget { margin-bottom: 20px; }
362
- .summary-widget details { border: 1px solid #e9ecef; border-radius: 10px; background: #f8f9fa; }
363
- .summary-widget summary { font-weight: 600; color: var(--bg-medium); padding: 12px 15px; cursor: pointer; outline: none; list-style: none; }
364
- .summary-widget summary::-webkit-details-marker { display: none; }
365
- .summary-widget summary:before { content: '▶'; margin-right: 8px; font-size: 0.8em; display: inline-block; transition: transform 0.2s; }
366
- .summary-widget details[open] summary:before { transform: rotate(90deg); }
367
- .summary-widget-content { padding: 0 15px 15px 15px; font-size: 0.9rem; }
368
- .summary-list { list-style: none; padding: 0; margin: 0; }
369
- .summary-list li { padding: 8px 0; border-bottom: 1px solid #e0e0e0; display: flex; justify-content: space-between; flex-wrap: wrap; gap: 10px; align-items: center; }
370
- .summary-list li:last-child { border-bottom: none; }
371
- .summary-list .env-id-small { font-weight: 700; color: var(--bg-medium); }
372
- .summary-list .timestamp { color: #555; font-size: 0.85rem; text-align: right; }
373
- .summary-list .keyword-small { font-style: italic; color: #666; font-size: 0.8rem; }
 
 
 
 
 
 
 
 
374
 
375
  @media (max-width: 768px) {
376
  .env-item { grid-template-columns: 1fr; gap: 12px; }
@@ -388,7 +396,6 @@ ADMHOSTO_TEMPLATE = '''
388
  .add-env-form .button { width: 100%; padding: 14px; }
389
 
390
  .stats-table th, .stats-table td { font-size: 0.75rem; padding: 6px 4px; }
391
- .summary-list .timestamp { text-align: left; }
392
  }
393
  </style>
394
  </head>
@@ -403,51 +410,47 @@ ADMHOSTO_TEMPLATE = '''
403
  {% endif %}
404
  {% endwith %}
405
 
406
- <div class="summary-widget">
407
- <details>
408
- <summary>Топ 5 сред по активности</summary>
409
- <div class="summary-widget-content">
410
- {% if top_5_environments %}
411
- <ul class="summary-list">
412
- {% for env in top_5_environments %}
 
413
  <li>
414
  <div>
415
- <span class="env-id-small">#{{ env.id }}</span>
416
- <span class="keyword-small">{{ env.keyword }}</span>
417
  </div>
418
- <span>{{ env.hits }} <i class="fas fa-eye fa-xs"></i></span>
419
  </li>
420
  {% endfor %}
421
  </ul>
422
  {% else %}
423
- <div class="empty-list-placeholder" style="padding:10px 0;">Нет данных</div>
424
  {% endif %}
425
  </div>
426
- </details>
427
- </div>
428
-
429
- <div class="summary-widget">
430
- <details>
431
- <summary>Последние 5 входов</summary>
432
- <div class="summary-widget-content">
433
- {% if last_5_entries %}
434
- <ul class="summary-list">
435
- {% for log in last_5_entries %}
436
  <li>
437
  <div>
438
- <span class="env-id-small">#{{ log.env_id }}</span>
439
- <span style="color:#888; font-size:0.8rem;">{{ log.ip }}</span>
440
  </div>
441
- <span class="timestamp">{{ log.time_str.split(' ')[1] }} <small style="color:#999">{{ log.time_str.split(' ')[0] }}</small></span>
442
  </li>
443
  {% endfor %}
444
  </ul>
445
- {% else %}
446
- <div class="empty-list-placeholder" style="padding:10px 0;">Нет данных</div>
447
  {% endif %}
448
  </div>
449
- </details>
450
- </div>
451
 
452
  <div class="section">
453
  <form method="POST" action="{{ url_for('create_environment') }}" class="add-env-form">
@@ -466,7 +469,6 @@ ADMHOSTO_TEMPLATE = '''
466
  <input type="text" id="search-env" placeholder="🔍 Поиск...">
467
  </div>
468
 
469
- <h2>Активные Среды</h2>
470
  <div class="section">
471
  {% if active_environments %}
472
  <ul class="env-list">
@@ -964,12 +966,12 @@ textarea {
964
  <select id="nationality">
965
  <option value="Eastern European">Восточная Европа</option>
966
  <option value="Northern European">Скандинавская</option>
 
967
  <option value="Asian">Азиатская</option>
968
  <option value="Latin American">Латиноамериканская</option>
969
- <option value="Mediterranean">Средиземноморская</option>
970
- <option value="African">Африканская</option>
971
  <option value="Middle Eastern">Ближневосточная</option>
972
  <option value="South Asian">Южноазиатская</option>
 
973
  <option value="Mixed Race">Смешанная</option>
974
  </select>
975
  </div>
@@ -1065,9 +1067,10 @@ textarea {
1065
  <select id="child_nationality">
1066
  <option value="Eastern European">Восточная Европа</option>
1067
  <option value="Northern European">Скандинавская</option>
1068
- <option value="Asian">Азиатская</option>
1069
  <option value="Mediterranean">Средиземноморская</option>
1070
- <option value="Mixed Race">Смешанная</option>
 
 
1071
  </select>
1072
  </div>
1073
  <div class="form-group">
@@ -1208,7 +1211,7 @@ const femaleHairstyles = {
1208
  };
1209
 
1210
  const maleHairstyles = {
1211
- 'long straight hair': 'Длинные прямые', 'short classic cut': 'Короткая классическая', 'fade haircut': 'Фейд', 'slicked back hair': 'Зачесанные назад', 'textured crop': 'Текстурированный кроп', 'quiff': 'Квифф', 'man bun': 'Мужской пучок', 'buzz cut': 'Под ноль', 'medium-length wavy hair': 'Волнистые средней длины', 'side part': 'С боковым пробором', 'undercut': 'Андеркат'
1212
  };
1213
 
1214
  function switchMode(mode) {
@@ -1222,12 +1225,15 @@ function switchMode(mode) {
1222
  document.getElementById('modeObjectBtn').classList.toggle('active', mode === 'object');
1223
  }
1224
 
1225
- function populateSelect(selectElement, options) {
1226
  selectElement.innerHTML = '';
1227
  for (const value in options) {
1228
  const option = document.createElement('option');
1229
  option.value = value;
1230
  option.textContent = options[value];
 
 
 
1231
  selectElement.appendChild(option);
1232
  }
1233
  }
@@ -1238,8 +1244,7 @@ function updateModelOptions() {
1238
  const hairstyleSelect = document.getElementById('hairstyle');
1239
 
1240
  populateSelect(bodyTypeSelect, gender === 'female' ? femaleBodyTypes : maleBodyTypes);
1241
- populateSelect(hairstyleSelect, gender === 'female' ? femaleHairstyles : maleHairstyles);
1242
- hairstyleSelect.value = 'long straight hair';
1243
  }
1244
 
1245
  function toggleOwnModel(isOwnModel) {
@@ -1427,57 +1432,56 @@ def admhosto():
1427
  data = load_data()
1428
  active_environments = []
1429
  archived_environments = []
1430
- all_logs = []
1431
 
1432
  for env_id, env_data in data.items():
1433
  if not isinstance(env_data, dict): continue
1434
-
1435
- if not env_data.get("archived"):
1436
- env_item = {
1437
- "id": env_id,
1438
- "keyword": env_data.get("keyword", "N/A"),
1439
- "type": env_data.get("type", "closed"),
1440
- "hits": env_data.get("hits", 0),
1441
- "created_at": env_data.get("created_at", ""),
1442
- "link": url_for('serve_env', env_id=env_id, _external=True)
1443
- }
1444
- active_environments.append(env_item)
1445
-
1446
- for log in env_data.get("logs", []):
1447
- try:
1448
- utc_dt = datetime.fromisoformat(log['time'])
1449
- almaty_dt = utc_dt + timedelta(hours=5)
1450
- log_item = {
1451
- "env_id": env_id,
1452
- "time": log["time"],
1453
- "time_str": almaty_dt.strftime('%Y-%m-%d %H:%M:%S'),
1454
- "ip": log.get("ip", "N/A")
1455
- }
1456
- all_logs.append(log_item)
1457
- except:
1458
- continue
1459
  else:
1460
- archived_item = {
1461
- "id": env_id,
1462
- "keyword": env_data.get("keyword", "N/A"),
1463
- "type": env_data.get("type", "closed"),
1464
- "created_at": env_data.get("created_at", ""),
1465
- }
1466
- archived_environments.append(archived_item)
1467
 
1468
  active_environments.sort(key=lambda x: x.get('created_at', ''), reverse=True)
1469
  archived_environments.sort(key=lambda x: x.get('created_at', ''), reverse=True)
1470
 
1471
- top_5_environments = sorted(active_environments, key=lambda x: x.get('hits', 0), reverse=True)[:5]
1472
- last_5_entries = sorted(all_logs, key=lambda x: x.get('time', ''), reverse=True)[:5]
1473
-
1474
- return render_template_string(
1475
- ADMHOSTO_TEMPLATE,
1476
- active_environments=active_environments,
1477
- archived_environments=archived_environments,
1478
- top_5_environments=top_5_environments,
1479
- last_5_entries=last_5_entries
1480
- )
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1481
 
1482
  @app.route('/admhosto/create', methods=['POST'])
1483
  def create_environment():
 
77
  **CONTEXT:** This is a professional, high-end commercial photoshoot for a children's clothing catalog or brand campaign. The overall atmosphere must be bright, clean, and joyful."""
78
  },
79
  "flagship_styles": {
80
+ "studio": "Impeccable high-fashion studio photoshoot. Bathed in flawless, soft, multi-point lighting that carves out every detail. Set against a seamless, infinite cyclorama wall (subtle off-white or light grey). The final image is ultra-high resolution, tack-sharp, with vibrant, true-to-life colors, emulating a major brand's advertising campaign.",
81
+ "street": "Dynamic, authentic street style shot in a bustling metropolis (e.g., Tokyo, New York). Cinematic, candid feel with beautiful natural urban lighting, authentic urban grit, and cinematic depth. The model should look effortlessly chic, integrated into the environment with a subtle motion blur suggesting movement and life.",
82
+ "lookbook": "Sophisticated and minimalist lookbook aesthetic. A clean, textured background (e.g., cast concrete, handmade paper, linen curtain). Soft, diffused, single-source light creating a gentle, sophisticated, and modern mood. The entire focus is on the garment's form, drape, and texture.",
83
+ "minimalism": "Extreme architectural minimalism. The model is a sculptural element against a monumental backdrop of brutalist concrete or stark, monolithic plaster. A single, dramatic, long shadow, cast by a harsh light source, creates a powerful and unforgettable graphic composition.",
84
+ "selfie": "**MANDATORY HYPERREALISM.** Imperfect, authentic 'moment-in-time' selfie. Shot on a smartphone, not a professional camera. Emulate real-world conditions: slight grain, natural skin textures with pores and minor imperfections (NO plastic skin), asymmetrical features. Include subtle environmental details like authentic reflections in a mirror, fingerprint smudges on the screen edge, or slight lens flare from a light source. The expression must be candid and genuine, not a posed 'influencer' look.",
85
+ "creative": "Avant-garde, conceptual, and visually arresting photoshoot. Utilizes unique, artistic props, dramatic and unconventional lighting, and a surreal background to create a visually striking, editorial-worthy image that tells a compelling and ambiguous story.",
86
+ "new_year": "Vibrant and festive New Year's atmosphere. The scene is illuminated by the soft, magical bokeh of fairy lights and dynamic, glowing sparkler trails. Set against a beautifully decorated, opulent tree or a magical snowy landscape under a starry sky. Evokes warmth, joy, and celebration.",
87
+ "retro": "Authentic 35mm film photograph emulation from the 1970s or 80s. Rich, warm analog grain, nostalgic color shifts, and subtle, dreamy light leaks. Poses, environment, and styling are perfectly period-correct, creating a powerful sense of nostalgia.",
88
+ "boho": "Ethereal golden hour boho dreamscape. Shot in a field of vibrant wildflowers during the final moments of sunset. The light is a warm, liquid gold, creating a soft, glowing aura around the model, highlighting natural textures and creating a serene, free-spirited, and magical vibe.",
89
+ "gothic": "Darkly romantic, moody, gothic atmosphere. Set in ancient, imposing architecture like a stone cathedral, castle ruins, or a grand library. Low-key, dramatic lighting creates deep, velvety shadows and a profound sense of mystery and timeless drama.",
90
+ "editorial": "High-fashion glossy magazine editorial. A bold, hyper-saturated, solid colored background commands attention. Clever use of multiple mirrors to create compelling, fragmented reflections and complex, artistic views of the model and outfit. A true statement image.",
91
+ "film_noir": "Cinematic black and white film noir scene. Extremely high contrast, dramatic 'chiaroscuro' lighting, casting long, stark shadows. An overwhelming sense of suspense and narrative. May incorporate atmospheric elements like dense fog, pouring rain, or Venetian blinds.",
92
+ "cottagecore": "Idyllic and romanticized cottagecore aesthetic. A cozy, rustic scene in a sun-dappled country house or a lush, overgrown garden. Beautiful, soft natural light streams through a window, highlighting organic textures and creating a feeling of wholesome, romantic, and peaceful rural life.",
93
+ "royalcore": "Opulent and regal royalcore aesthetic. Set within a lavish, historic palace interior featuring ornate gold details, rich velvet curtains, and gilded antique furniture. The lighting is grand and dramatic, as if from a chandelier, creating an unmistakable air of aristocracy and old-world luxury.",
94
+ "solarpunk": "Vibrant, optimistic solarpunk future. Sleek, futuristic architecture with organic curves is seamlessly integrated with lush, vertical greenery. Bright, clean, hopeful light fills the scene, suggesting a utopian, tech-advanced society living in harmony with nature.",
95
+ "skater": "Dynamic and energetic skater aesthetic. A wide-angle, low-perspective shot in a gritty, sun-bleached skate park or on urban streets. Captures explosive movement, raw youthful energy, and a real, counter-culture spirit.",
96
+ "baroque": "A dramatic, living Baroque painting. An ornate, richly detailed setting with overflowing textures like silk, velvet, and brocade. The lighting is high-contrast and theatrical, reminiscent of a Caravaggio masterpiece, creating deep, intense colors and an epic, painterly quality.",
97
+ "japandi": "A serene and tranquil Japandi style interior. A perfect fusion of Japanese minimalism and Scandinavian functionality. Clean lines, a muted neutral palette, natural wood, and a focus on handcrafted details create a space of ultimate calm and uncluttered beauty.",
98
+ "coastal": "Effortlessly chic 'coastal grandmother' style. A bright, airy, sun-filled setting by the sea. A clean palette of whites, beiges, and soft blues. Natural materials like linen and weathered wood create a feeling of relaxed, timeless, seaside elegance.",
99
+ "cyberpunk": "A gritty, neon-drenched cyberpunk cityscape at night. High-tech, futuristic elements are everywhere, with brilliant, colorful reflections from countless neon signs on wet, reflective streets. A cool, cinematic color palette and a sense of awe-inspiring urban dystopia.",
100
+ "fantasy": "An enchanting and otherworldly fantasy scene. A magical, bioluminescent forest, ancient vine-covered ruins, or an ethereal, dreamlike landscape. The lighting is mystical and magical, creating a narrative-driven image that feels like a still from an epic fantasy film.",
101
+ "90s_grunge": "Raw, authentic 90s grunge aesthetic. A backdrop of urban decay, an abandoned warehouse, or a graffiti-covered alley. A desaturated, moody color palette. Captures a feeling of angst, rebellion, and effortless, non-conformist style.",
102
+ "techwear": "Sleek, functional, and futuristic Techwear style. Set against imposing, modern, urban architecture. The lighting is clean, sharp, and slightly cool, highlighting the technical details, innovative fabrics, and functionality of the garments.",
103
+ "avant_garde": "Experimental, high-concept avant-garde fashion. A composition of abstract shapes, bold and jarring color clashes, and unconventional poses. A highly artistic and conceptual approach that challenges all traditional notions of beauty and style.",
104
+ "home_casual": "A cozy, authentic, lived-in home setting. Soft, beautiful, natural light streaming through a large window. A relaxed, intimate atmosphere filled with personal details like books, plants, and comfortable, textured furnishings.",
105
+ "social_media_candid": "An authentic, 'Instagrammable' candid moment. Shot in a trendy cafe with great lighting or during a walk through a picturesque street. Looks completely spontaneous and natural, as if capturing a real, unfiltered moment in time.",
106
+ "backstage": "The hectic, atmospheric, and energetic backstage of a high-fashion show. Racks of designer clothes, glowing makeup stations, and a palpable sense of focused energy. The lighting is functional but chaotic, creating a compelling 'behind-the-scenes' narrative.",
107
+ "road_trip": "A cinematic American West road trip aesthetic. The model leans against a classic vintage car, set against a vast, epic landscape at sunset. A powerful sense of freedom, adventure, wanderlust, and golden-hued nostalgia.",
108
+ "rainy_day": "A romantic, melancholic, and beautiful rainy day scene. Glistening reflections on wet city pavement, artistic droplets on windows, and the soft, diffused light of an overcast sky. A cozy, thoughtful, and introspective mood.",
109
+ "night_flash": "Edgy, high-energy, direct-flash night photography. Creates high contrast, deeply saturated colors, and sharp, graphic shadows. A raw, spontaneous, 'paparazzi' or intimate party-snapshot feeling.",
110
+ "golden_hour_picnic": "An idyllic and romantic golden hour picnic. Warm, glowing sunset light filters dreamily through the trees. A beautifully styled picnic scene creates a relaxed, joyful, and deeply romantic atmosphere.",
111
+ "beach": "Vibrant and serene beach scene. Shot during the magical golden hour with soft, warm, glowing sunlight. The model is near the sparkling water with gentle waves and fine, golden sand, creating a relaxed, sun-kissed, and utterly beautiful atmosphere."
112
  },
113
  "object_styles": {
114
+ "studio": "Immaculate product photography on a seamless, neutral background (white, grey, or beige). Perfect, diffused, multi-point lighting that eliminates all harsh shadows and reveals every microscopic detail of the product's texture, form, and material.",
115
+ "minimalism": "An artistic, minimalist composition on a highly textured surface like raw concrete, luxe marble, or fine sand. A single, crisp, hard light source creates a single, long, graphic shadow, emphasizing the product's pure silhouette and form.",
116
+ "nature": "The product is artfully and harmoniously placed in a complementary natural environment. E.g., resting on mossy rocks in a misty forest, beside a clear, flowing stream, or nestled among vibrant flowers. The lighting is soft, natural, and enhances the organic, fresh feel.",
117
+ "luxe": "A sophisticated luxury still life. The product is arranged on a rich, tactile surface like deep-colored silk, plush velvet, or dark, veined marble. The lighting is low-key, moody, and sophisticated, with soft, elegant highlights that suggest opulence, exclusivity, and desire.",
118
+ "dark": "A moody and dramatic 'dark academia' aesthetic. The product is set against a dark, richly textured background like aged wood or a leather-bound book. A single, directional light source carves the product out of the deep shadows, creating a mysterious, intense, and intellectual atmosphere."
119
  }
120
  }
121
  with open(PROMPTS_FILE, 'w', encoding='utf-8') as f:
 
358
  .empty-list-placeholder { text-align:center; padding: 20px; color: #888; }
359
  .no-margin { margin-bottom: 0; }
360
 
361
+ .dashboard { border: 1px solid #e0e0e0; border-radius: 10px; margin-bottom: 25px; }
362
+ .dashboard summary { padding: 15px; font-weight: 600; color: var(--bg-medium); cursor: pointer; list-style: none; display: flex; justify-content: space-between; align-items: center; }
363
+ .dashboard summary::-webkit-details-marker { display: none; }
364
+ .dashboard summary:after { content: '\\f078'; font-family: 'Font Awesome 6 Free'; font-weight: 900; transition: transform 0.2s; }
365
+ .dashboard[open] summary:after { transform: rotate(180deg); }
366
+ .dashboard-content { padding: 0 15px 15px; display: flex; flex-direction: column; gap: 20px; border-top: 1px solid #e0e0e0; }
367
+ .dashboard-section h3 { font-size: 1rem; margin-top: 15px; margin-bottom: 10px; color: #333; }
368
+ .dashboard-list { list-style: none; padding: 0; margin: 0; font-size: 0.9rem; }
369
+ .dashboard-list li { display: flex; justify-content: space-between; align-items: center; padding: 8px 0; border-bottom: 1px solid #f0f0f0; }
370
+ .dashboard-list li:last-child { border-bottom: none; }
371
+ .dashboard-list .env-id-small { font-weight: 600; color: var(--bg-medium); }
372
+ .dashboard-list .keyword-small { color: #777; font-style: italic; white-space: nowrap; overflow: hidden; text-overflow: ellipsis; max-width: 150px; }
373
+ .dashboard-list .hits-small { font-weight: 500; }
374
+ .dashboard-list .time-small { color: #777; font-size: 0.8rem; }
375
+
376
+ @media (min-width: 601px) {
377
+ .dashboard-content { flex-direction: row; justify-content: space-between; align-items: flex-start; }
378
+ .dashboard-section { flex: 1; min-width: 0; }
379
+ .dashboard-section:first-child { padding-right: 15px; border-right: 1px solid #e0e0e0;}
380
+ .dashboard-section:last-child { padding-left: 15px;}
381
+ }
382
 
383
  @media (max-width: 768px) {
384
  .env-item { grid-template-columns: 1fr; gap: 12px; }
 
396
  .add-env-form .button { width: 100%; padding: 14px; }
397
 
398
  .stats-table th, .stats-table td { font-size: 0.75rem; padding: 6px 4px; }
 
399
  }
400
  </style>
401
  </head>
 
410
  {% endif %}
411
  {% endwith %}
412
 
413
+ <details class="dashboard">
414
+ <summary>Сводка</summary>
415
+ <div class="dashboard-content">
416
+ <div class="dashboard-section">
417
+ <h3><i class="fas fa-fire"></i> Топ-5 активных сред</h3>
418
+ {% if top_5_envs %}
419
+ <ul class="dashboard-list">
420
+ {% for env in top_5_envs %}
421
  <li>
422
  <div>
423
+ <span class="env-id-small">{{ env.id }}</span>
424
+ <span class="keyword-small" title="{{ env.keyword }}">{{ env.keyword }}</span>
425
  </div>
426
+ <span class="hits-small">{{ env.hits }} <i class="fas fa-eye fa-xs"></i></span>
427
  </li>
428
  {% endfor %}
429
  </ul>
430
  {% else %}
431
+ <div class="empty-list-placeholder" style="padding: 5px 0;">Нет данных</div>
432
  {% endif %}
433
  </div>
434
+ <div class="dashboard-section">
435
+ <h3><i class="fas fa-history"></i> Последние 5 входов</h3>
436
+ {% if last_5_logs %}
437
+ <ul class="dashboard-list">
438
+ {% for log in last_5_logs %}
 
 
 
 
 
439
  <li>
440
  <div>
441
+ <span class="env-id-small">{{ log.env_id }}</span>
442
+ <span class="time-small">{{ log.time.split(' ')[1] }}</span>
443
  </div>
444
+ <span class="time-small">{{ log.time.split(' ')[0] }}</span>
445
  </li>
446
  {% endfor %}
447
  </ul>
448
+ {% else %}
449
+ <div class="empty-list-placeholder" style="padding: 5px 0;">Нет данных</div>
450
  {% endif %}
451
  </div>
452
+ </div>
453
+ </details>
454
 
455
  <div class="section">
456
  <form method="POST" action="{{ url_for('create_environment') }}" class="add-env-form">
 
469
  <input type="text" id="search-env" placeholder="🔍 Поиск...">
470
  </div>
471
 
 
472
  <div class="section">
473
  {% if active_environments %}
474
  <ul class="env-list">
 
966
  <select id="nationality">
967
  <option value="Eastern European">Восточная Европа</option>
968
  <option value="Northern European">Скандинавская</option>
969
+ <option value="Mediterranean">Средиземноморская</option>
970
  <option value="Asian">Азиатская</option>
971
  <option value="Latin American">Латиноамериканская</option>
 
 
972
  <option value="Middle Eastern">Ближневосточная</option>
973
  <option value="South Asian">Южноазиатская</option>
974
+ <option value="African">Африканская</option>
975
  <option value="Mixed Race">Смешанная</option>
976
  </select>
977
  </div>
 
1067
  <select id="child_nationality">
1068
  <option value="Eastern European">Восточная Европа</option>
1069
  <option value="Northern European">Скандинавская</option>
 
1070
  <option value="Mediterranean">Средиземноморская</option>
1071
+ <option value="Asian">Азиатская</option>
1072
+ <option value="South Asian">Южноазиатская</option>
1073
+ <option value="African">Африканская</option>
1074
  </select>
1075
  </div>
1076
  <div class="form-group">
 
1211
  };
1212
 
1213
  const maleHairstyles = {
1214
+ 'short classic cut': 'Короткая классическая', 'fade haircut': 'Фейд', 'slicked back hair': 'Зачесанные назад', 'textured crop': 'Текстурированный кроп', 'quiff': 'Квифф', 'man bun': 'Мужской пучок', 'buzz cut': 'Под ноль', 'medium-length wavy hair': 'Волнистые средней длины', 'long straight hair': 'Длинные прямые', 'side part': 'С боковым пробором', 'undercut': 'Андеркат'
1215
  };
1216
 
1217
  function switchMode(mode) {
 
1225
  document.getElementById('modeObjectBtn').classList.toggle('active', mode === 'object');
1226
  }
1227
 
1228
+ function populateSelect(selectElement, options, defaultValue = null) {
1229
  selectElement.innerHTML = '';
1230
  for (const value in options) {
1231
  const option = document.createElement('option');
1232
  option.value = value;
1233
  option.textContent = options[value];
1234
+ if (value === defaultValue) {
1235
+ option.selected = true;
1236
+ }
1237
  selectElement.appendChild(option);
1238
  }
1239
  }
 
1244
  const hairstyleSelect = document.getElementById('hairstyle');
1245
 
1246
  populateSelect(bodyTypeSelect, gender === 'female' ? femaleBodyTypes : maleBodyTypes);
1247
+ populateSelect(hairstyleSelect, gender === 'female' ? femaleHairstyles : maleHairstyles, gender === 'female' ? 'long straight hair' : null);
 
1248
  }
1249
 
1250
  function toggleOwnModel(isOwnModel) {
 
1432
  data = load_data()
1433
  active_environments = []
1434
  archived_environments = []
 
1435
 
1436
  for env_id, env_data in data.items():
1437
  if not isinstance(env_data, dict): continue
1438
+ env_item = {
1439
+ "id": env_id,
1440
+ "keyword": env_data.get("keyword", "N/A"),
1441
+ "type": env_data.get("type", "closed"),
1442
+ "hits": env_data.get("hits", 0),
1443
+ "created_at": env_data.get("created_at", ""),
1444
+ "link": url_for('serve_env', env_id=env_id, _external=True)
1445
+ }
1446
+ if env_data.get("archived"):
1447
+ archived_environments.append(env_item)
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1448
  else:
1449
+ active_environments.append(env_item)
 
 
 
 
 
 
1450
 
1451
  active_environments.sort(key=lambda x: x.get('created_at', ''), reverse=True)
1452
  archived_environments.sort(key=lambda x: x.get('created_at', ''), reverse=True)
1453
 
1454
+ top_5_envs = sorted([e for e in active_environments if e.get('hits', 0) > 0], key=lambda x: x['hits'], reverse=True)[:5]
1455
+
1456
+ all_logs = []
1457
+ for env_id, env_data in data.items():
1458
+ if env_data and isinstance(env_data.get('logs'), list):
1459
+ for log in env_data['logs']:
1460
+ if isinstance(log, dict):
1461
+ log_copy = log.copy()
1462
+ log_copy['env_id'] = env_id
1463
+ all_logs.append(log_copy)
1464
+
1465
+ sorted_logs = sorted(all_logs, key=lambda x: x.get('time', ''), reverse=True)
1466
+
1467
+ last_5_logs = []
1468
+ for log in sorted_logs[:5]:
1469
+ try:
1470
+ utc_dt = datetime.fromisoformat(log['time'])
1471
+ almaty_dt = utc_dt + timedelta(hours=5)
1472
+ time_str = almaty_dt.strftime('%Y-%m-%d %H:%M:%S')
1473
+ last_5_logs.append({
1474
+ "env_id": log.get('env_id', 'N/A'),
1475
+ "time": time_str
1476
+ })
1477
+ except (ValueError, TypeError):
1478
+ continue
1479
+
1480
+ return render_template_string(ADMHOSTO_TEMPLATE,
1481
+ active_environments=active_environments,
1482
+ archived_environments=archived_environments,
1483
+ top_5_envs=top_5_envs,
1484
+ last_5_logs=last_5_logs)
1485
 
1486
  @app.route('/admhosto/create', methods=['POST'])
1487
  def create_environment():