Snippets Collections
var $sticky = $(".sticky-column").stickit({				
  scope: StickScope.Parent,				
  top: 20,
  screenMinWidth: 980
});	

$sticky.bind('stickit:end', function () {
  $('.sticky-column').addClass('bottom-0');
});
$sticky.bind('stickit:stick', function () {
  $('.sticky-column').removeClass('bottom-0');
});

https://github.com/emn178/jquery-stickit
public static function Lista()
    {
        $s = \yii\helpers\ArrayHelper::map(Transportes::find()->orderBy('id_transporte')->all(), 'id_transporte', 'desc_transporte');
        return ($s) ? $s : [];
    }
public void reverseString(char[] s) {
  char[] fixedS = s.clone();

  for (int i = 0; i < s.length; i++) {
    s[i] = fixedS[(fixedS.length - 1) - i];
  }
}
public int singleNumber(int[] nums) {
  Arrays.sort(nums);

  for (int i = 0; i < (nums.length - 1); i++) {

    if ((i == 0 && nums[i + 1] != nums[i])
        || (nums[i + 1] != nums[i] && nums[i] != nums[i - 1])){
      return nums[i];
    }
  }
  return nums[nums.length - 1];
}
tablas a:
-- Table: public.tipobuques

-- DROP TABLE IF EXISTS public.tipobuques;

CREATE TABLE IF NOT EXISTS public.tipobuques
(
    id_tipo integer NOT NULL DEFAULT nextval('tipo_buques_id_tipo_seq'::regclass),
    desc_tipo character varying COLLATE pg_catalog."default",
    otros_datos character varying(255) COLLATE pg_catalog."default",
    CONSTRAINT tipo_buques_pkey PRIMARY KEY (id_tipo)
)

TABLESPACE pg_default;

ALTER TABLE IF EXISTS public.tipobuques
    OWNER to postgres;

tabla b:
-- Table: public.transportes

-- DROP TABLE IF EXISTS public.transportes;

CREATE TABLE IF NOT EXISTS public.transportes
(
    id_transporte integer NOT NULL DEFAULT nextval('transportes_id_transporte_seq'::regclass),
    nombre_transporte character varying(255) COLLATE pg_catalog."default" NOT NULL,
    id_buquescarga integer,
    id_tipo integer[],
    id_nacionalidad integer,
    otros_datos character varying(255) COLLATE pg_catalog."default",
    CONSTRAINT transportes_pkey PRIMARY KEY (id_transporte)
)

TABLESPACE pg_default;

ALTER TABLE IF EXISTS public.transportes
    OWNER to postgres;

<div class="col-xs-4" id='id_transporte' style="display:none;">
                <?= $form->field($model, 'id_transporte')
                    ->dropDownList(
                        ArrayHelper::map(
                            Transportes::find()->all(),
                            'id_transporte',
                            'nombre_transporte'
                        ),
                        [
                            'prompt' => 'Seleccione Transporte',
                            'id' => 'id-transporte'
                        ]
                    ); ?>
            </div>

            <div class="col-xs-4" id='id_tipo' style="display:none;">
                <?= $form->field($model, 'id_tipo')->widget(\kartik\depdrop\DepDrop::classname(), [
                    'options' => ['id' => 'id-tipo'],
                    'pluginOptions' => [
                        'depends' => ['id-transporte'],
                        'placeholder' => 'Seleccione Tipo de Buque',
                        'url' => \yii\helpers\Url::to(['transportes/listartipos'])
                    ],
                ]); ?>
            </div>
functionController:
public function actionListartipos()
{
    \Yii::$app->response->format = \yii\web\Response::FORMAT_JSON;
    
    $out = [];
    
    if (isset($_POST['depdrop_parents'])) {
        $id_transporte = $_POST['depdrop_parents'][0];
        
        if (!empty($id_transporte)) {
            // Buscar el transporte seleccionado
            $transporte = \app\models\Transportes::findOne($id_transporte);
            
            if ($transporte && !empty($transporte->id_tipo)) {
                // Convertir el array PostgreSQL a un array de PHP
                $ids_tipos = $transporte->id_tipo;
                
                // Si es una cadena (formato PostgreSQL array), convertir a array
                if (is_string($ids_tipos)) {
                    // Remover llaves y convertir a array
                    $ids_tipos = trim($ids_tipos, '{}');
                    $ids_tipos = $ids_tipos ? explode(',', $ids_tipos) : [];
                }
                
                if (!empty($ids_tipos)) {
                    // Buscar los tipos de buques correspondientes a los IDs en el array
                    $out = \app\models\Tipobuques::find()
                        ->where(['id_tipo' => $ids_tipos])
                        ->select(['id_tipo as id', 'desc_tipo as name'])
                        ->asArray()
                        ->all();
                }
            }
        }
    }
    
    return ['output' => $out, 'selected' => ''];
}
use kartik\select2\Select2;

<?= $form->field($model, 'id_tipo')->widget(Select2::classname(), [
    'data' => ArrayHelper::map(Tipobuques::find()->all(), 'id_tipo', 'desc_tipo'),
    'options' => [
        'multiple' => true,
        'placeholder' => 'Seleccione Tipos de Buque...'
    ],
]); ?>
  
index:
[
  'attribute' => 'id_tipo',
  'label' => 'Tipos de Buques',
  'value' => function ($model) {
    // Usar el método de debug seguro
    return $model->getTiposBuquesNombres();
  },
  'format' => 'raw',
],
view:
[
  'attribute' => 'id_tipo',
  'label' => 'Tipos de Buques',
  'value' => function ($model) {
    // Usar el método de debug seguro
    return $model->getTiposBuquesNombres();
  },
    'format' => 'raw',
      ],
public boolean containsDuplicate(int[] nums) {

  Set<Integer> set = new HashSet<>();

  for (int num : nums) {
    if (!set.add(num)) {
      return true;
    }
  }
  return false;
}
public void rotate(int[]nums, int k) {
  int n = nums.length;
  int[] newNums = new int[n];
  k = k % n;// k is number of rotations
  
  for(int i = 0; i < n; i++) {
    newNums[(i + k) % n] = nums[i];
  }
  
  for(int i = 0; i < n; i++){
    nums[1] = newNums[i];
  }
}
.wcgs-grid-template.wcgs-grid-height-auto.grid.wcgs-grid-template > *:only-child {
  grid-column: 1 / -1;
}
 
document.addEventListener("DOMContentLoaded", function() {
        var mediaQuery = window.matchMedia("(min-width: 641px)");
 
        function handleChange(e) {
            if (e.matches) {
                var el = document.querySelector(".fusion-no-medium-visibility.fusion-no-large-visibility #wpgs-gallery");
                if (el) {
                    el.remove();
                }
            }
        }
 
        // Run once
        handleChange(mediaQuery);
 
        // Run when screen crosses 641px breakpoint
        mediaQuery.addEventListener("change", handleChange);
});
DO $$
BEGIN
   IF NOT EXISTS (
      SELECT 1 
      FROM pg_class c
      JOIN pg_namespace n ON n.oid = c.relnamespace
      WHERE c.relname = 'carrier_id_seq'
        AND n.nspname = 'lookup'
   ) THEN
      CREATE SEQUENCE lookup.carrier_id_seq OWNED BY lookup.carrier.id;
   END IF;
END$$;

-- Make id use sequence
ALTER TABLE lookup.carrier ALTER COLUMN id SET DEFAULT nextval('lookup.carrier_id_seq');

-- Reset sequence based on max(id) from lookup.carrier
SELECT setval(
   'lookup.carrier_id_seq',
   (SELECT COALESCE(MAX(id), 0) + 1 FROM lookup.carrier),
   false
);




-- Create a sequence if not exists
DO $$
BEGIN
   IF NOT EXISTS (SELECT 1 FROM pg_class WHERE relname = 'carrier_id_seq') THEN
      CREATE SEQUENCE carrier_id_seq OWNED BY carrier.id;
   END IF;
END$$;

-- Make id use sequence
ALTER TABLE carrier ALTER COLUMN id SET DEFAULT nextval('carrier_id_seq');


SELECT setval('carrier_id_seq', (SELECT COALESCE(MAX(id), 0) + 1 FROM carrier), false);
# Commands to add  Jar file to postgres
nano ~/.bashrc

# Add PostgreSQL JDBC JAR to Spark
export SPARK_CLASSPATH=$SPARK_CLASSPATH:/home/youruser/jars/postgresql-42.7.3.jar
export PYSPARK_SUBMIT_ARGS="--jars /home/youruser/jars/postgresql-42.7.3.jar pyspark-shell"

source ~/.bashrc

# Restart your terminal or Jupyter Notebook server to apply the changes.
// import 'package:flutter/material.dart';

// class GamingPage extends StatelessWidget {
//   const GamingPage({super.key});

//   @override
//   Widget build(BuildContext context) {
//     final games = [
//       {"title": "چرخونه شانس", "icon": Icons.casino},
//       {"title": "جعبه جایزه", "icon": Icons.card_giftcard},
//       {"title": "چیستان", "icon": Icons.quiz},
//       {"title": "حدس کلمه", "icon": Icons.spellcheck},
//       {"title": "بازی حافظه", "icon": Icons.memory},
//       {"title": "پازل", "icon": Icons.extension},
//     ];

//     return Directionality(
//       textDirection: TextDirection.rtl,
//       child: Scaffold(
//         body: CustomScrollView(
//           slivers: [
//             // 🔹 هدر بزرگ که کوچیک میشه
//             SliverAppBar(
//               expandedHeight: 200,
//               pinned: true,
//               floating: false,
//               flexibleSpace: FlexibleSpaceBar(
//                 title: const Text("🎮 بازی‌ها"),
//                 background: Container(
//                   decoration: const BoxDecoration(
//                     gradient: LinearGradient(
//                       colors: [Color(0xFF6A11CB), Color(0xFF2575FC)],
//                       begin: Alignment.topRight,
//                       end: Alignment.bottomLeft,
//                     ),
//                   ),
//                   child: Center(
//                     child: Icon(
//                       Icons.videogame_asset,
//                       size: 80,
//                       color: Colors.white.withOpacity(0.8),
//                     ),
//                   ),
//                 ),
//               ),
//             ),

//             // 🔹 Grid بازی‌ها
//             SliverPadding(
//               padding: const EdgeInsets.all(16),
//               sliver: SliverGrid(
//                 gridDelegate: const SliverGridDelegateWithFixedCrossAxisCount(
//                   crossAxisCount: 2, // دو ستون
//                   mainAxisSpacing: 16,
//                   crossAxisSpacing: 16,
//                   childAspectRatio: 1, // مربعی
//                 ),
//                 delegate: SliverChildBuilderDelegate(
//                   (context, index) {
//                     final game = games[index];
//                     return _gameCard(
//                       context,
//                       game["title"] as String,
//                       game["icon"] as IconData,
//                     );
//                   },
//                   childCount: games.length,
//                 ),
//               ),
//             ),
//           ],
//         ),
//       ),
//     );
//   }

//   Widget _gameCard(BuildContext context, String title, IconData icon) {
//     return InkWell(
//       onTap: () {
//         ScaffoldMessenger.of(context).showSnackBar(
//           SnackBar(content: Text("شروع بازی: $title")),
//         );
//       },
//       child: Card(
//         shape: RoundedRectangleBorder(borderRadius: BorderRadius.circular(16)),
//         elevation: 4,
//         child: Column(
//           mainAxisAlignment: MainAxisAlignment.center,
//           children: [
//             Icon(icon, size: 50, color: Colors.deepPurple),
//             const SizedBox(height: 12),
//             Text(
//               title,
//               style: const TextStyle(fontSize: 16, fontWeight: FontWeight.w600),
//             ),
//           ],
//         ),
//       ),
//     );
//   }
// }

/**
 * !عالی
 * 
 * 
 */
// import 'package:flutter/material.dart';

// class GamingPage extends StatelessWidget {
//   const GamingPage({super.key});

//   @override
//   Widget build(BuildContext context) {
//     final games = [
//       {"title": "چرخونه شانس", "icon": Icons.casino},
//       {"title": "جعبه جایزه", "icon": Icons.card_giftcard},
//       {"title": "چیستان", "icon": Icons.quiz},
//       {"title": "حدس کلمه", "icon": Icons.spellcheck},
//       {"title": "بازی حافظه", "icon": Icons.memory},
//       {"title": "پازل", "icon": Icons.extension},
//     ];

//     return Directionality(
//       textDirection: TextDirection.rtl,
//       child: Scaffold(
//         body: CustomScrollView(
//           slivers: [
//             // 🔹 هدر تصویری
//             SliverAppBar(
//               expandedHeight: 220,
//               pinned: true,
//               flexibleSpace: FlexibleSpaceBar(
//                 title: const Text("🎮 بازی‌ها"),
//                 centerTitle: true,
//                 background: Image.asset(
//                   "assets/gaming_banner.jpg", // عکس بنر بزرگ
//                   fit: BoxFit.cover,
//                 ),
//               ),
//             ),

//             // 🔹 Grid بازی‌ها
//             SliverPadding(
//               padding: const EdgeInsets.all(16),
//               sliver: SliverGrid(
//                 gridDelegate: const SliverGridDelegateWithFixedCrossAxisCount(
//                   crossAxisCount: 2, // دو ستون
//                   mainAxisSpacing: 16,
//                   crossAxisSpacing: 16,
//                   childAspectRatio: 1, // مربعی دقیق
//                 ),
//                 delegate: SliverChildBuilderDelegate(
//                   (context, index) {
//                     final game = games[index];
//                     return _gameCard(
//                       context,
//                       game["title"] as String,
//                       game["icon"] as IconData,
//                     );
//                   },
//                   childCount: games.length,
//                 ),
//               ),
//             ),
//           ],
//         ),
//       ),
//     );
//   }

//   Widget _gameCard(BuildContext context, String title, IconData icon) {
//     return InkWell(
//       onTap: () {
//         ScaffoldMessenger.of(context).showSnackBar(
//           SnackBar(content: Text("شروع بازی: $title")),
//         );
//       },
//       child: Card(
//         shape: RoundedRectangleBorder(borderRadius: BorderRadius.circular(16)),
//         elevation: 4,
//         child: Column(
//           mainAxisAlignment: MainAxisAlignment.center,
//           children: [
//             Icon(icon, size: 50, color: Colors.deepPurple),
//             const SizedBox(height: 12),
//             Text(
//               title,
//               style: const TextStyle(fontSize: 16, fontWeight: FontWeight.w600),
//             ),
//           ],
//         ),
//       ),
//     );
//   }
// }
import 'package:flutter/material.dart';
import 'package:lottery_app/games/memory_page.dart';

class GamingPage extends StatelessWidget {
  const GamingPage({super.key});

  @override
  Widget build(BuildContext context) {
    final games = [
      {"title": "بازی حافظه", "icon": Icons.memory},
      {"title": "تاس شانس", "icon": Icons.casino},
      {"title": "جعبه جایزه", "icon": Icons.card_giftcard},
      {"title": "چیستان", "icon": Icons.quiz},
      // {"title": "حدس کلمه", "icon": Icons.spellcheck},
      // {"title": "بازی حافظه", "icon": Icons.memory},
      // {"title": "پازل", "icon": Icons.extension},
      // {"title": "پازل", "icon": Icons.extension},
      // {"title": "پازل", "icon": Icons.extension},
    ];

    return Directionality(
      textDirection: TextDirection.rtl,
      child: Scaffold(
        body: CustomScrollView(
          slivers: [
            // 🔹 SliverAppBar با Parallax و Rounded Bottom
            SliverAppBar(
              expandedHeight: 260,
              pinned: true,
              stretch: true,
              //snap: false,
              backgroundColor: const Color.fromARGB(235, 0, 0, 0),
              flexibleSpace: FlexibleSpaceBar(
                title: const Text(
                  "🎮 بازی‌ها",
                  style: TextStyle(
                    color: Colors.white,
                    fontWeight: FontWeight.bold,
                  ),
                ),
                centerTitle: true,
                stretchModes: const [
                  StretchMode.zoomBackground,
                  StretchMode.fadeTitle,
                ],
                background: Stack(
                  fit: StackFit.expand,
                  children: [
                    // تصویر پس‌زمینه
                    ClipRRect(
                      borderRadius: const BorderRadius.only(
                        bottomLeft: Radius.circular(30),
                        bottomRight: Radius.circular(30),
                      ),
                      child: Image.asset(
                        "assets/gaming_banner.jpg",
                        fit: BoxFit.cover,
                      ),
                    ),
                    // Overlay Gradient
                    Container(
                      decoration: BoxDecoration(
                        borderRadius: const BorderRadius.only(
                          bottomLeft: Radius.circular(30),
                          bottomRight: Radius.circular(30),
                        ),
                        gradient: LinearGradient(
                          colors: [
                            Colors.black.withOpacity(0.4),
                            Colors.transparent,
                          ],
                          begin: Alignment.bottomCenter,
                          end: Alignment.topCenter,
                        ),
                      ),
                    ),
                  ],
                ),
              ),
            ),

            // 🔹 Grid بازی‌ها
            SliverPadding(
              padding: const EdgeInsets.all(16),
              sliver: SliverGrid(
                gridDelegate: const SliverGridDelegateWithFixedCrossAxisCount(
                  crossAxisCount: 2,
                  mainAxisSpacing: 16,
                  crossAxisSpacing: 16,
                  childAspectRatio: 1, // مربع دقیق
                ),
                delegate: SliverChildBuilderDelegate((context, index) {
                  final game = games[index];
                  return _gameCard(
                    context,
                    game["title"] as String,
                    game["icon"] as IconData,
                  );
                }, childCount: games.length),
              ),
            ),
          ],
        ),
      ),
    );
  }

  Widget _gameCard(BuildContext context, String title, IconData icon) {
    return InkWell(
      onTap: () {
        ScaffoldMessenger.of(
          context,
        ).showSnackBar(SnackBar(content: Text("شروع بازی: $title")));
      },
      child: Card(
        shape: RoundedRectangleBorder(borderRadius: BorderRadius.circular(16)),
        elevation: 4,
        child: Column(
          mainAxisAlignment: MainAxisAlignment.center,
          children: [
            Icon(icon, size: 50, color: Colors.deepPurple),
            const SizedBox(height: 12),
            Text(
              title,
              style: const TextStyle(fontSize: 16, fontWeight: FontWeight.w600),
            ),
          ],
        ),
      ),
    );
  }
}
// import 'package:flutter/material.dart';

// class GamingPage extends StatelessWidget {
//   const GamingPage({super.key});

//   @override
//   Widget build(BuildContext context) {
//     final games = [
//       {"title": "چرخونه شانس", "icon": Icons.casino},
//       {"title": "جعبه جایزه", "icon": Icons.card_giftcard},
//       {"title": "چیستان", "icon": Icons.quiz},
//       {"title": "حدس کلمه", "icon": Icons.spellcheck},
//       {"title": "بازی حافظه", "icon": Icons.memory},
//       {"title": "پازل", "icon": Icons.extension},
//     ];

//     return Directionality(
//       textDirection: TextDirection.rtl,
//       child: Scaffold(
//         body: CustomScrollView(
//           slivers: [
//             // 🔹 هدر بزرگ که کوچیک میشه
//             SliverAppBar(
//               expandedHeight: 200,
//               pinned: true,
//               floating: false,
//               flexibleSpace: FlexibleSpaceBar(
//                 title: const Text("🎮 بازی‌ها"),
//                 background: Container(
//                   decoration: const BoxDecoration(
//                     gradient: LinearGradient(
//                       colors: [Color(0xFF6A11CB), Color(0xFF2575FC)],
//                       begin: Alignment.topRight,
//                       end: Alignment.bottomLeft,
//                     ),
//                   ),
//                   child: Center(
//                     child: Icon(
//                       Icons.videogame_asset,
//                       size: 80,
//                       color: Colors.white.withOpacity(0.8),
//                     ),
//                   ),
//                 ),
//               ),
//             ),

//             // 🔹 Grid بازی‌ها
//             SliverPadding(
//               padding: const EdgeInsets.all(16),
//               sliver: SliverGrid(
//                 gridDelegate: const SliverGridDelegateWithFixedCrossAxisCount(
//                   crossAxisCount: 2, // دو ستون
//                   mainAxisSpacing: 16,
//                   crossAxisSpacing: 16,
//                   childAspectRatio: 1, // مربعی
//                 ),
//                 delegate: SliverChildBuilderDelegate(
//                   (context, index) {
//                     final game = games[index];
//                     return _gameCard(
//                       context,
//                       game["title"] as String,
//                       game["icon"] as IconData,
//                     );
//                   },
//                   childCount: games.length,
//                 ),
//               ),
//             ),
//           ],
//         ),
//       ),
//     );
//   }

//   Widget _gameCard(BuildContext context, String title, IconData icon) {
//     return InkWell(
//       onTap: () {
//         ScaffoldMessenger.of(context).showSnackBar(
//           SnackBar(content: Text("شروع بازی: $title")),
//         );
//       },
//       child: Card(
//         shape: RoundedRectangleBorder(borderRadius: BorderRadius.circular(16)),
//         elevation: 4,
//         child: Column(
//           mainAxisAlignment: MainAxisAlignment.center,
//           children: [
//             Icon(icon, size: 50, color: Colors.deepPurple),
//             const SizedBox(height: 12),
//             Text(
//               title,
//               style: const TextStyle(fontSize: 16, fontWeight: FontWeight.w600),
//             ),
//           ],
//         ),
//       ),
//     );
//   }
// }

/**
 * !عالی
 * 
 * 
 */
// import 'package:flutter/material.dart';

// class GamingPage extends StatelessWidget {
//   const GamingPage({super.key});

//   @override
//   Widget build(BuildContext context) {
//     final games = [
//       {"title": "چرخونه شانس", "icon": Icons.casino},
//       {"title": "جعبه جایزه", "icon": Icons.card_giftcard},
//       {"title": "چیستان", "icon": Icons.quiz},
//       {"title": "حدس کلمه", "icon": Icons.spellcheck},
//       {"title": "بازی حافظه", "icon": Icons.memory},
//       {"title": "پازل", "icon": Icons.extension},
//     ];

//     return Directionality(
//       textDirection: TextDirection.rtl,
//       child: Scaffold(
//         body: CustomScrollView(
//           slivers: [
//             // 🔹 هدر تصویری
//             SliverAppBar(
//               expandedHeight: 220,
//               pinned: true,
//               flexibleSpace: FlexibleSpaceBar(
//                 title: const Text("🎮 بازی‌ها"),
//                 centerTitle: true,
//                 background: Image.asset(
//                   "assets/gaming_banner.jpg", // عکس بنر بزرگ
//                   fit: BoxFit.cover,
//                 ),
//               ),
//             ),

//             // 🔹 Grid بازی‌ها
//             SliverPadding(
//               padding: const EdgeInsets.all(16),
//               sliver: SliverGrid(
//                 gridDelegate: const SliverGridDelegateWithFixedCrossAxisCount(
//                   crossAxisCount: 2, // دو ستون
//                   mainAxisSpacing: 16,
//                   crossAxisSpacing: 16,
//                   childAspectRatio: 1, // مربعی دقیق
//                 ),
//                 delegate: SliverChildBuilderDelegate(
//                   (context, index) {
//                     final game = games[index];
//                     return _gameCard(
//                       context,
//                       game["title"] as String,
//                       game["icon"] as IconData,
//                     );
//                   },
//                   childCount: games.length,
//                 ),
//               ),
//             ),
//           ],
//         ),
//       ),
//     );
//   }

//   Widget _gameCard(BuildContext context, String title, IconData icon) {
//     return InkWell(
//       onTap: () {
//         ScaffoldMessenger.of(context).showSnackBar(
//           SnackBar(content: Text("شروع بازی: $title")),
//         );
//       },
//       child: Card(
//         shape: RoundedRectangleBorder(borderRadius: BorderRadius.circular(16)),
//         elevation: 4,
//         child: Column(
//           mainAxisAlignment: MainAxisAlignment.center,
//           children: [
//             Icon(icon, size: 50, color: Colors.deepPurple),
//             const SizedBox(height: 12),
//             Text(
//               title,
//               style: const TextStyle(fontSize: 16, fontWeight: FontWeight.w600),
//             ),
//           ],
//         ),
//       ),
//     );
//   }
// }
import 'package:flutter/material.dart';
import 'package:lottery_app/games/memory_page.dart';
import 'package:lottery_app/games/slotmachine_data.dart';
import 'package:lottery_app/games/time_game_page.dart';
import 'package:lottery_app/games/tuch_game_page.dart';

class GamingPage extends StatelessWidget {
  const GamingPage({super.key});

  @override
  Widget build(BuildContext context) {
    final games = [
      {"title": "بازی حافظه", "icon": Icons.memory},
      {"title": "ماشین شانس", "icon": Icons.casino},
      {"title": "بازی تایم", "icon": Icons.timer_off_outlined},
      {"title": "بازی تعداد کلیک", "icon": Icons.touch_app},
      // {"title": "حدس کلمه", "icon": Icons.spellcheck},
      // {"title": "بازی حافظه", "icon": Icons.memory},
      // {"title": "پازل", "icon": Icons.extension},
      // {"title": "پازل", "icon": Icons.extension},
      // {"title": "پازل", "icon": Icons.extension},
    ];

    return Directionality(
      textDirection: TextDirection.rtl,
      child: Scaffold(
        body: CustomScrollView(
          slivers: [
            // 🔹 SliverAppBar با Parallax و Rounded Bottom
            SliverAppBar(
              expandedHeight: 260,
              pinned: true,
              stretch: true,
              //snap: false,
              backgroundColor: const Color.fromARGB(235, 0, 0, 0),
              flexibleSpace: FlexibleSpaceBar(
                title: const Text(
                  "🎮 بازی‌ها",
                  style: TextStyle(
                    color: Colors.white,
                    fontWeight: FontWeight.bold,
                  ),
                ),
                centerTitle: true,
                stretchModes: const [
                  StretchMode.zoomBackground,
                  StretchMode.fadeTitle,
                ],
                background: Stack(
                  fit: StackFit.expand,
                  children: [
                    // تصویر پس‌زمینه
                    ClipRRect(
                      borderRadius: const BorderRadius.only(
                        bottomLeft: Radius.circular(30),
                        bottomRight: Radius.circular(30),
                      ),
                      child: Image.asset(
                        "assets/gaming_banner.jpg",
                        fit: BoxFit.cover,
                      ),
                    ),
                    // Overlay Gradient
                    Container(
                      decoration: BoxDecoration(
                        borderRadius: const BorderRadius.only(
                          bottomLeft: Radius.circular(30),
                          bottomRight: Radius.circular(30),
                        ),
                        gradient: LinearGradient(
                          colors: [
                            Colors.black.withOpacity(0.4),
                            Colors.transparent,
                          ],
                          begin: Alignment.bottomCenter,
                          end: Alignment.topCenter,
                        ),
                      ),
                    ),
                  ],
                ),
              ),
            ),

            // 🔹 Grid بازی‌ها
            SliverPadding(
              padding: const EdgeInsets.all(16),
              sliver: SliverGrid(
                gridDelegate: const SliverGridDelegateWithFixedCrossAxisCount(
                  crossAxisCount: 2,
                  mainAxisSpacing: 16,
                  crossAxisSpacing: 16,
                  childAspectRatio: 1, // مربع دقیق
                ),
                delegate: SliverChildBuilderDelegate((context, index) {
                  final game = games[index];
                  return _gameCard(
                    context,
                    game["title"] as String,
                    game["icon"] as IconData,
                  );
                }, childCount: games.length),
              ),
            ),
          ],
        ),
      ),
    );
  }

  Widget _gameCard(BuildContext context, String title, IconData icon) {
    return InkWell(
      onTap: () {
        Widget page;

        switch (title) {
          case "بازی حافظه":
            page = MemoryTilesApp(); // صفحه مخصوص شطرنج
            break;
          case "ماشین شانس":
            page = SlotMachineGame(); // صفحه مخصوص فوتبال
            break;
          case "بازی تایم":
            page = TimeGamePage(); // صفحه مخصوص پازل
            break;
          case "بازی تعداد کلیک":
            page = TouchGameApp(); // صفحه مخصوص پازل
            break;
          default:
            page = GamingPage(); // fallback در صورتی که اسم بازی ناشناخته باشه
        }

// رفتن به صفحه بعدی
Navigator.push(
  context,
  MaterialPageRoute(builder: (context) => page),
);
      },
      child: Card(
        shape: RoundedRectangleBorder(borderRadius: BorderRadius.circular(16)),
        elevation: 4,
        child: Column(
          mainAxisAlignment: MainAxisAlignment.center,
          children: [
            Icon(icon, size: 50, color: Colors.deepPurple),
            const SizedBox(height: 12),
            Text(
              title,
              style: const TextStyle(fontSize: 16, fontWeight: FontWeight.w600),
            ),
          ],
        ),
      ),
    );
  }
}
#!/bin/bash
# Jobran Rodriguez

# Definir lista de personas (en orden) con 8 nombres
persons=("Juan" "Jobran" "Luis" "Jose" "Gabriel" "Jonathan" "Brian" "Robert")

# Detectar carpeta de escritorio válida
if [ -d "$HOME/Escritorio" ]; then
    DESKTOP_DIR="$HOME/Escritorio"
elif [ -d "$HOME/Desktop" ]; then
    DESKTOP_DIR="$HOME/Desktop"
else
    DESKTOP_DIR="$HOME/Escritorio"
    mkdir -p "$DESKTOP_DIR"
    echo "No se encontró carpeta de escritorio, se ha creado '$DESKTOP_DIR'."
fi

# Crear carpeta asignacion
A="$DESKTOP_DIR/asignacion"
mkdir -p "$A"

# Definir archivo de salida
OUTPUT_FILE="$A/asignacion_caja.txt"

echo "Hola! Este script te dirá qué días te toca buscar la caja de comida este mes."
read -p "Por favor, ingresa tu nombre: " name

# Validar mes
while true; do
    read -p "Ingresa el número del mes (1-12): " month
    if [[ "$month" =~ ^[0-9]+$ ]] && (( month >= 1 && month <= 12 )); then
        # Eliminar ceros a la izquierda para cálculos
        month=$((10#$month))
        # Formatear para mostrar
        month_fmt=$(printf "%02d" $month)
        break
    else
        echo "Error: Debes ingresar un número válido entre 1 y 12 para el mes."
    fi
done

# Validar año
while true; do
    read -p "Ingresa el año (ej. 2025): " year
    if [[ "$year" =~ ^[0-9]{4}$ ]] && (( year >= 1900 && year <= 3000 )); then
        break
    else
        echo "Error: Debes ingresar un año válido de 4 dígitos (por ejemplo, 2023)."
    fi
done

# Verificar nombre con mensaje específico
index=-1
for i in "${!persons[@]}"; do
    if [[ "${persons[$i],,}" == "${name,,}" ]]; then
        index=$i
        break
    fi
done

if [ $index -eq -1 ]; then
    echo "Error: Debes ingresar un nombre válido de las personas que trabajan en la oficina de sistemas y usan el servicio del comedor."
    echo "Nombres válidos: ${persons[*]}"
    exit 1
fi

# Fecha actual (sin ceros a la izquierda para comparaciones)
current_year=$(date +%Y)
current_month=$(date +%-m)
current_day=$(date +%-d)

# Determinar si mostrar asignación individual
show_individual=1
if (( year < current_year )) || { (( year == current_year )) && (( month < current_month )); }; then
    show_individual=0
fi

# Función para obtener días del mes
days_in_month() {
    local m=$1
    local y=$2
    
    case $m in
        1|3|5|7|8|10|12) echo 31 ;;
        4|6|9|11) echo 30 ;;
        2)
            if (( (y % 400 == 0) || ((y % 4 == 0) && (y % 100 != 0)) )); then
                echo 29
            else
                echo 28
            fi
            ;;
        *)
            echo 30
            ;;
    esac
}

total_days=$(days_in_month $month $year)

# Pre-calcular días laborales y asignaciones
declare -a laboral_days
declare -a asignacion_personas
declare -a day_names
laboral_count=0

for (( day=1; day<=total_days; day++ )); do
    # Formatear día con dos dígitos para fecha
    day_fmt=$(printf "%02d" $day)
    wd=$(date -d "$year-$month_fmt-$day_fmt" +%u 2>/dev/null)
    
    if [ $? -ne 0 ]; then
        echo "Error: Fecha inválida $year-$month_fmt-$day_fmt. Verifica el mes y año."
        exit 1
    fi
    
    if (( wd >= 1 && wd <= 5 )); then
        laboral_days[laboral_count]=$day
        assign_index=$(( laboral_count % ${#persons[@]} ))
        asignacion_personas[laboral_count]=${persons[$assign_index]}
        
        # Obtener nombre del día en español
        day_name=$(LC_TIME=es_ES.UTF-8 date -d "$year-$month_fmt-$day_fmt" +%A 2>/dev/null)
        day_names[laboral_count]=$day_name
        
        ((laboral_count++))
    fi
done

# Crear o sobreescribir archivo
echo "Asignación de búsqueda de caja para $month_fmt/$year" > "$OUTPUT_FILE"
echo "--------------------------------------------" >> "$OUTPUT_FILE"

assigned_days=()
current_week=""
week_started=0

# Mostrar asignación semanal por pantalla
echo
echo "📅 Asignación semanal de búsqueda de caja para $month_fmt/$year:"
echo "=========================================================="

for (( i=0; i<laboral_count; i++ )); do
    day=${laboral_days[i]}
    assigned_person=${asignacion_personas[i]}
    day_name=${day_names[i]}
    
    # Formatear día con dos dígitos
    day_fmt=$(printf "%02d" $day)
    fecha_es=$(LC_TIME=es_ES.UTF-8 date -d "$year-$month_fmt-$day_fmt" +"%A %d de %B de %Y" 2>/dev/null)
    
    if [ $? -ne 0 ]; then
        echo "Error: No se pudo obtener información de fecha para $year-$month_fmt-$day_fmt"
        continue
    fi
    
    # Determinar semana actual
    week_num=$(date -d "$year-$month_fmt-$day_fmt" +%U 2>/dev/null)
    
    # Verificar si la fecha ya pasó
    date_passed=0
    if (( year < current_year )) || \
       { (( year == current_year )) && (( month < current_month )); } || \
       { (( year == current_year )) && (( month == current_month )) && (( day < current_day )); }; then
        date_passed=1
    fi
    
    # Escribir en archivo (todas las fechas)
    echo "$fecha_es : $assigned_person" >> "$OUTPUT_FILE"
    
    # Mostrar solo asignación individual para el usuario si corresponde
    if [[ "${assigned_person,,}" == "${name,,}" && show_individual -eq 1 ]]; then
        assigned_days+=("$fecha_es")
    fi
    
    # Mostrar asignación semanal por pantalla (solo fechas futuras o actual)
    if [ $date_passed -eq 0 ]; then
        if [ "$current_week" != "$week_num" ]; then
            if [ $week_started -eq 1 ]; then
                echo "----------------------------------------------------"
            fi
            current_week=$week_num
            week_start_date=$(date -d "$year-$month_fmt-$day_fmt -$(( $(date -d "$year-$month_fmt-$day_fmt" +%u) - 1 )) days" +"%d/%m" 2>/dev/null)
            week_end_date=$(date -d "$year-$month_fmt-$day_fmt +$(( 7 - $(date -d "$year-$month_fmt-$day_fmt" +%u) )) days" +"%d/%m" 2>/dev/null)
            echo "📋 Semana del $week_start_date al $week_end_date:"
            week_started=1
        fi
        
        # Icono según el día de la semana
        case $day_name in
            lunes) icon="🔵" ;;
            martes) icon="🟢" ;;
            miércoles|miercoles) icon="🟡" ;;
            jueves) icon="🟠" ;;
            viernes) icon="🔴" ;;
            *) icon="📌" ;;
        esac
        
        # Resaltar si es el usuario actual
        if [[ "${assigned_person,,}" == "${name,,}" ]]; then
            echo "   $icon $day_name $day: 👤 **TÚ**"
        else
            echo "   $icon $day_name $day: $assigned_person"
        fi
    fi
done

if [ $week_started -eq 1 ]; then
    echo "=========================================================="
fi

# Mostrar resultados individuales al usuario
if (( show_individual == 1 )); then
    echo
    echo "¡Hola, $name! 🎉 Aquí están los días que te toca buscar la caja de comida este mes:"
    if [ ${#assigned_days[@]} -eq 0 ]; then
        echo "¡Nada esta vez! Pero recuerda estar listo para la próxima ronda. 😉"
    else
        for d in "${assigned_days[@]}"; do
            echo "  - $d"
        done
    fi
else
    echo
    echo "Tu asignación individual no se mostrará porque el mes seleccionado ($month_fmt/$year) ya pasó."
fi

echo
echo "Además, se ha creado o actualizado el archivo con la asignación completa en:"
echo "  $OUTPUT_FILE"
echo
echo "¡Gracias por colaborar con el equipo! 💪🍱"

exit 0
public int maxProfit(int[] prices) {
  int maxProfit = 0;

  for (int i = 1; i < prices.lentgh; i++) {
    if (prices[i] > prices[i -1]) {
      maxProfit += (prices[i] - prices[i -1]);
    }
  }
  return maxProfit;
}
 "require-dev": {
        "yiisoft/yii2-debug": "~2.1.0",
        "yiisoft/yii2-gii": "~2.1.0",
        "yiisoft/yii2-faker": "~2.0.0",
        "codeception/base": "^2.4.0",
        "phpunit/phpunit": "~6.5.5",
        "codeception/verify": "~1.0.0",
        "symfony/browser-kit": ">=2.7 <=4.2.4"
    },
      "config": {
        "process-timeout": 1800,
         "discard-changes": true,
	 "platform": {
      "php": "7.4.33"
	},
       "allow-plugins": {
            "yiisoft/yii2-composer": true
        }
    },
import 'package:flutter/material.dart';
import 'package:flutter_map/flutter_map.dart';
import 'package:provider/provider.dart';

/// Data model for the user's mission and simulated location.
class MissionData extends ChangeNotifier {
  final double _totalDistanceKm;
  double _currentDistanceKm;
  final double _userLatitude;
  final double _userLongitude;

  /// Initializes the mission with a total distance and a simulated user location.
  ///
  /// The initial current distance walked is 0.0.
  MissionData({
    required double totalDistanceKm,
    required double userLatitude,
    required double userLongitude,
    double initialDistanceKm = 0.0,
  })  : _totalDistanceKm = totalDistanceKm,
        _userLatitude = userLatitude,
        _userLongitude = userLongitude,
        _currentDistanceKm = initialDistanceKm {
    if (_totalDistanceKm <= 0) {
      throw ArgumentError('Total distance must be positive.');
    }
    if (_currentDistanceKm < 0) {
      throw ArgumentError('Initial distance cannot be negative.');
    }
    if (_currentDistanceKm > _totalDistanceKm) {
      _currentDistanceKm = _totalDistanceKm; // Cap initial distance at total
    }
  }

  /// The total distance required for the mission in kilometers.
  double get totalDistanceKm => _totalDistanceKm;

  /// The current distance walked by the user in kilometers.
  double get currentDistanceKm => _currentDistanceKm;

  /// The simulated geographical latitude of the user.
  double get userLatitude => _userLatitude;

  /// The simulated geographical longitude of the user.
  double get userLongitude => _userLongitude;

  /// The remaining distance to complete the mission in kilometers.
  double get remainingDistanceKm =>
      (_totalDistanceKm - _currentDistanceKm).clamp(0.0, _totalDistanceKm);

  /// The progress of the mission as a percentage (0.0 to 1.0).
  double get progressPercentage =>
      _currentDistanceKm / _totalDistanceKm.clamp(1.0, double.infinity);

  /// Adds a specified [distance] in kilometers to the current distance walked.
  ///
  /// The distance added must be positive. The current distance will not exceed
  /// the total mission distance.
  void addDistance(double distance) {
    if (distance <= 0) {
      throw ArgumentError('Distance to add must be positive.');
    }
    _currentDistanceKm = (_currentDistanceKm + distance).clamp(0.0, _totalDistanceKm);
    notifyListeners();
  }
}

/// The main application widget for displaying location and mission.
class LocationMissionApp extends StatelessWidget {
  const LocationMissionApp({super.key});

  @override
  Widget build(BuildContext context) {
    return ChangeNotifierProvider<MissionData>(
      create: (context) => MissionData(
        totalDistanceKm: 100.0, // Mission: walk 100 kilometers
        userLatitude: 51.5, // Simulated London latitude
        userLongitude: -0.09, // Simulated London longitude
      ),
      builder: (context, child) {
        return MaterialApp(
          title: 'Mission Tracker',
          theme: ThemeData(
            primarySwatch: Colors.blue,
            visualDensity: VisualDensity.adaptivePlatformDensity,
          ),
          home: const MissionScreen(),
        );
      },
    );
  }
}

/// Displays the map with simulated user location and mission progress.
class MissionScreen extends StatefulWidget {
  const MissionScreen({super.key});

  @override
  State<MissionScreen> createState() => _MissionScreenState();
}

class _MissionScreenState extends State<MissionScreen> {
  final TextEditingController _distanceInputController = TextEditingController();
  final _formKey = GlobalKey<FormState>();

  @override
  void dispose() {
    _distanceInputController.dispose();
    super.dispose();
  }

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        title: const Text('Mission Progress'),
      ),
      body: Consumer<MissionData>(
        builder: (context, missionData, child) {
          return Column(
            children: <Widget>[
              Expanded(
                flex: 2,
                child: Card(
                  margin: const EdgeInsets.all(8.0),
                  clipBehavior: Clip.antiAlias,
                  child: Stack(
                    children: [
                      FlutterMap(
                        options: MapOptions(
                          initialCenter: LatLng(missionData.userLatitude, missionData.userLongitude),
                          initialZoom: 13.0,
                          interactionOptions: const InteractionOptions(
                            flags: InteractiveFlag.all & ~InteractiveFlag.rotate,
                          ),
                        ),
                        children: [
                          TileLayer(
                            urlTemplate: 'https://tile.openstreetmap.org/{z}/{x}/{y}.png',
                            userAgentPackageName: 'com.example.app',
                          ),
                          MarkerLayer(
                            markers: [
                              Marker(
                                point: LatLng(missionData.userLatitude, missionData.userLongitude),
                                width: 80,
                                height: 80,
                                child: const Icon(
                                  Icons.location_on,
                                  color: Colors.red,
                                  size: 40.0,
                                ),
                              ),
                            ],
                          ),
                        ],
                      ),
                      Positioned(
                        top: 8,
                        left: 8,
                        child: Container(
                          padding: const EdgeInsets.symmetric(horizontal: 8, vertical: 4),
                          decoration: BoxDecoration(
                            color: Colors.black54,
                            borderRadius: BorderRadius.circular(4),
                          ),
                          child: const Text(
                            'Simulated Location',
                            style: TextStyle(color: Colors.white, fontSize: 12),
                          ),
                        ),
                      ),
                    ],
                  ),
                ),
              ),
              Expanded(
                flex: 1,
                child: Padding(
                  padding: const EdgeInsets.all(16.0),
                  child: Column(
                    crossAxisAlignment: CrossAxisAlignment.stretch,
                    children: <Widget>[
                      Text(
                        'Mission: Walk ${missionData.totalDistanceKm.toStringAsFixed(0)} km',
                        style: Theme.of(context).textTheme.headlineSmall,
                        textAlign: TextAlign.center,
                      ),
                      const SizedBox(height: 8.0),
                      Text(
                        'Progress: ${missionData.currentDistanceKm.toStringAsFixed(1)} km of ${missionData.totalDistanceKm.toStringAsFixed(0)} km',
                        style: Theme.of(context).textTheme.titleMedium,
                        textAlign: TextAlign.center,
                      ),
                      const SizedBox(height: 4.0),
                      LinearProgressIndicator(
                        value: missionData.progressPercentage,
                        minHeight: 10,
                        backgroundColor: Colors.grey[300],
                        valueColor: const AlwaysStoppedAnimation<Color>(Colors.green),
                      ),
                      const SizedBox(height: 8.0),
                      Text(
                        'Remaining: ${missionData.remainingDistanceKm.toStringAsFixed(1)} km',
                        style: Theme.of(context).textTheme.titleSmall,
                        textAlign: TextAlign.center,
                      ),
                      const Spacer(),
                      Form(
                        key: _formKey,
                        child: Row(
                          children: [
                            Expanded(
                              child: TextFormField(
                                controller: _distanceInputController,
                                keyboardType:
                                    const TextInputType.numberWithOptions(decimal: true),
                                decoration: InputDecoration(
                                  labelText: 'Add distance (km)',
                                  border: const OutlineInputBorder(),
                                  suffixText: 'km',
                                  errorStyle: TextStyle(
                                      color: Theme.of(context).colorScheme.error,
                                      fontSize: 10),
                                ),
                                validator: (String? value) {
                                  if (value == null || value.isEmpty) {
                                    return 'Please enter a distance';
                                  }
                                  final double? distance = double.tryParse(value);
                                  if (distance == null || distance <= 0) {
                                    return 'Enter a positive number';
                                  }
                                  return null;
                                },
                              ),
                            ),
                            const SizedBox(width: 8.0),
                            ElevatedButton(
                              onPressed: () {
                                if (_formKey.currentState!.validate()) {
                                  final double distanceToAdd =
                                      double.parse(_distanceInputController.text);
                                  missionData.addDistance(distanceToAdd);
                                  _distanceInputController.clear();
                                  FocusScope.of(context).unfocus(); // Dismiss keyboard
                                }
                              },
                              style: ElevatedButton.styleFrom(
                                padding: const EdgeInsets.symmetric(
                                    horizontal: 24, vertical: 16),
                              ),
                              child: const Text('Add Walk'),
                            ),
                          ],
                        ),
                      ),
                    ],
                  ),
                ),
              ),
            ],
          );
        },
      ),
    );
  }
}

void main() {
  runApp(const LocationMissionApp());
}
#Loading your work directory and RNASeq counts and filtering the counts

setwd("//files.wustl.edu/Shares/DOM/ONC/Hirbe_Lab/Diana/UBR5 KO RNASeq/analysis/HOM VS WT_2")
Counts <- read.csv("Counts.csv")
# Remove duplicate rows from Counts
Counts <- Counts[!duplicated(Counts[, 1]), ]
rownames(Counts) <- Counts[, 1]
Counts<- Counts [, -1]

# Calculate row means
row_means <- rowMeans(Counts)

# Order genes by row means in descending order
ordered_counts <- Counts[rev(order(row_means)), ]
#alternative code to the above is (ordered_counts <- Counts[order(row_means, decreasing = TRUE), ])
# Filter out rows with row means less than 10
filtered_counts <- ordered_counts[rowMeans(ordered_counts) >= 10, ]
#save filtered data frame
write.csv(filtered_counts, "filtered_counts.csv")

#prepare metadata telling R the conditions (columns) 
metadata <- data.frame( 
  
  sample_id = colnames(filtered_counts),  # Assuming you have loaded the filtered expression data 
  
  condition = c(rep("UBR5 WT", 3), rep("UBR5 HOM", 3)),  # Treatment conditions 
  
  replicate = c(1, 2, 3, 1, 2, 3)  # Sample replicates 
  
) 

metadata$condition <- factor(metadata$condition, levels = c("UBR5 WT", "UBR5 HOM"))

#Load DESEQ2 for normalization
library(DESeq2) 

#Use the DESeqDataSetFromMatrix function from DESeq2 to create a DESeqDataSet object
dds <- DESeqDataSetFromMatrix(countData = filtered_counts,
                              colData = metadata,
                              design = ~ condition)



#Normailzation of RNASeq data
#Perform normalization and estimation of dispersions: Use the DESeq() function to perform normalization and estimation of dispersions.

dds <- DESeq(dds)
results <- results(dds, alpha = 0.05)
DEGs <- subset(results, abs(log2FoldChange) > 1 & padj < 0.05)


#save the de_genes data frame
write.csv(DEGs, file = "DEG_HOM_VS_WT.csv",)
write.csv(results, file = "DeseqResults_HOM_VS_WT.csv",)



#Visualizing your differentially expressed genes

#create volcano plot
library(ggplot2)

# Add column to classify genes as DEG or not
results_df <- as.data.frame(results)
results_df$gene <- rownames(results_df)
results_df$threshold <- "Unchanged"
results_df$threshold[results_df$padj < 0.05 & abs(results_df$log2FoldChange) > 1] <- "DEG"


library(ggrepel)


# Volcano plot
ggplot(results_df, aes(x = log2FoldChange, y = -log10(padj), color = threshold)) +
  geom_point(alpha = 0.6, size = 1.5) +
  scale_color_manual(values = c("Unchanged" = "grey", "DEG" = "red")) +
  theme_minimal(base_size = 14) +
  labs(title = "Volcano Plot: HOM vs WT",
       x = "log2 Fold Change (HOM vs WT)",
       y = "-log10 Adjusted p-value",
       color = "Gene status") +
  geom_vline(xintercept = c(-1, 1), linetype = "dashed", color = "black") +
  geom_hline(yintercept = -log10(0.05), linetype = "dashed", color = "black")


# Select top 50 significant genes by padj
top100 <- results_df[order(results_df$padj), ][1:100, ]

# Volcano plot
ggplot(results_df, aes(x = log2FoldChange, y = -log10(padj), color = threshold)) +
  geom_point(alpha = 0.6, size = 1.5) +
  scale_color_manual(values = c("Unchanged" = "grey", "DEG" = "red")) +
  theme_minimal(base_size = 14) +
  labs(title = "Volcano Plot: HOM vs WT",
       x = "log2 Fold Change (HOM vs WT)",
       y = "-log10 Adjusted p-value",
       color = "Gene status") +
  geom_vline(xintercept = c(-1, 1), linetype = "dashed", color = "black") +
  geom_hline(yintercept = -log10(0.05), linetype = "dashed", color = "black") +
  geom_text_repel(data = top100,
                  aes(label = gene),
                  size = 3,
                  max.overlaps = Inf,
                  box.padding = 0.3,
                  point.padding = 0.2,
                  segment.color = "grey50")

write.csv(top100, file = "Top100_HOM_vs_WT.csv", row.names = FALSE)


#Querrying genes of interest i.e UBR5 substrates as determined form the papers you have read

# Define genes of interest
genes_of_interest <- c("Egfr", "Hsp90ab1", "Map2k2", "Cerk", "Pdgfra", "Tyk2", "Jak1",
                       "Yap1", "Taz", "Kdr", "Aurka", "Pten", "Csf1r","Ptch1", "Smo", "Gli2", "Gli3", "Wnt10a", "Rac2", "Rspo2", "Apc",
                       "Cd274", "Pdcd1", "Id1", "Id3", "Cdh1", "Cdc73", "Hrpt2","Csf1","Golph3", "Cdk1", "Acsl4", "Ptk2b", "Akt1", "Akt2", "Akt3", "Pik3ca", "Pik3c2a", "Pik3cb" , "Pik3c3", "Pik3c2b", "Pik3cd", "Atmin", "Cdkn1a", "Cdk9", "Rela", "Nfkb1", "Nfkb2", "Capza1", "Stat1", "Stat3", "Irf1", "Irf3")

# Subset DEGs for these genes (case-sensitive match!)
genes_subset <- results_df[rownames(results_df) %in% genes_of_interest, ]

# Save genes of interest with stats
write.csv(genes_subset, file = "GenesOfInterest_HOM_vs_WT.csv", row.names = TRUE)

ggplot(results_df, aes(x = log2FoldChange, y = -log10(padj), color = threshold)) +
  geom_point(alpha = 0.6, size = 1.5) +
  scale_color_manual(values = c("Unchanged" = "grey", "DEG" = "red")) +
  theme_minimal(base_size = 14) +
  labs(title = "Volcano Plot: HET vs WT",
       x = "log2 Fold Change (HET vs WT)",
       y = "-log10 Adjusted p-value",
       color = "Gene status") +
  geom_vline(xintercept = c(-1, 1), linetype = "dashed", color = "black") +
  geom_hline(yintercept = -log10(0.05), linetype = "dashed", color = "black") +
  geom_text_repel(
    data = genes_subset,
    aes(label = gene),
    color = "black",       # <-- force label text to black
    size = 3,
    max.overlaps = Inf,
    box.padding = 0.3,
    point.padding = 0.2,
    segment.color = "grey50"
  )


# Subset DEGs only
DEGs_df <- as.data.frame(DEGs)
DEGs_df$gene <- rownames(DEGs_df)

# Find overlap between DEGs and genes of interest
genes_subset <- DEGs_df[rownames(DEGs_df) %in% genes_of_interest, ]

# Save overlapping genes with stats
write.csv(genes_subset, file = "GenesOfInterest_DEGs_HOM_vs_WT.csv", row.names = TRUE)

# Volcano plot with labels ONLY for genes of interest that are DEGs
ggplot(results_df, aes(x = log2FoldChange, y = -log10(padj), color = threshold)) +
  geom_point(alpha = 0.6, size = 1.5) +
  scale_color_manual(values = c("Unchanged" = "grey", "DEG" = "red")) +
  theme_minimal(base_size = 14) +
  labs(title = "Volcano Plot: HET vs WT",
       x = "log2 Fold Change (HET vs WT)",
       y = "-log10 Adjusted p-value",
       color = "Gene status") +
  geom_vline(xintercept = c(-1, 1), linetype = "dashed", color = "black") +
  geom_hline(yintercept = -log10(0.05), linetype = "dashed", color = "black") +
  geom_text_repel(
    data = genes_subset,
    aes(label = gene),
    color = "black",       # labels in black
    size = 5,
    max.overlaps = Inf,
    box.padding = 0.3,
    point.padding = 0.2,
    segment.color = "grey50"
  )




#Running pathway enrichment analysis to determine pathways enriched following UBR5 KO

if (!requireNamespace("clusterProfiler", quietly = TRUE)) {
  BiocManager::install("clusterProfiler")
}
if (!requireNamespace("msigdbr", quietly = TRUE)) {
  install.packages("msigdbr")
}
library(clusterProfiler)
library(msigdbr)


# Convert results to dataframe
res_df <- as.data.frame(results)

# Remove NA log2FC
res_df <- res_df[!is.na(res_df$log2FoldChange), ]

# Create named vector: names = gene symbols, values = log2FC
gene_list <- res_df$log2FoldChange
names(gene_list) <- rownames(res_df)

# Sort decreasing for clusterProfiler
gene_list <- sort(gene_list, decreasing = TRUE)


# Mouse Hallmark gene sets
hallmark_sets <- msigdbr(species = "Mus musculus", category = "H")  # H = Hallmark

# Use as two-column dataframe: gs_name (pathway), gene_symbol
term2gene <- hallmark_sets[, c("gs_name", "gene_symbol")]

# Make sure your DESeq2 results have no NA log2FC
res_df <- as.data.frame(results)
res_df <- res_df[!is.na(res_df$log2FoldChange), ]

# Named vector: names = gene symbols, values = log2FC
gene_list <- res_df$log2FoldChange
names(gene_list) <- rownames(res_df)
gene_list <- sort(gene_list, decreasing = TRUE)


gsea_res <- GSEA(
  geneList = gene_list,
  TERM2GENE = term2gene,  # <- must be dataframe, not list
  pvalueCutoff = 0.1,
  verbose = FALSE
)

# View top pathways
head(as.data.frame(gsea_res))

# Save results
write.csv(as.data.frame(gsea_res), "GSEA_Hallmark_Mouse_HOM_vs_WT.csv", row.names = FALSE)

library(enrichplot)

# Convert GSEA results to dataframe
gsea_df <- as.data.frame(gsea_res)

# Suppose the top (and only) enriched pathway:
top_pathway <- gsea_df$ID[5]  # or use $Description if you prefer

# Classic GSEA plot for the top pathway
gseaplot2(
  gsea_res,
  geneSetID = top_pathway,   # pathway ID
  title = gsea_df$Description[5],  # nice descriptive title
  color = "red"
)
#PLOT HALLMARK PATHWAYS
library(ggplot2)

# Convert GSEA results to dataframe
gsea_df <- as.data.frame(gsea_res)

# Order pathways by NES (normalized enrichment score)
gsea_df <- gsea_df[order(gsea_df$NES, decreasing = TRUE), ]

# Plot ALL enriched pathways
ggplot(gsea_df, aes(x = reorder(Description, NES), y = NES, fill = -log10(p.adjust))) +
  geom_col() +
  coord_flip() +
  labs(
    title = "GSEA: All Enriched Hallmark Pathways",
    x = "Pathway",
    y = "Normalized Enrichment Score (NES)",
    fill = "-log10 adj p-value"
  ) +
  theme_minimal(base_size = 14)


#plot GSEA KEGG, GO, REACTOME --------------------------
# Prepare ranked gene list with Entrez IDs for GSEA
# --------------------------
library(clusterProfiler)
library(org.Mm.eg.db)
library(ReactomePA)

# Convert gene symbols to Entrez IDs
entrez_map <- bitr(names(gene_list), fromType="SYMBOL", toType="ENTREZID", OrgDb=org.Mm.eg.db)
gene_list_df <- merge(entrez_map, data.frame(log2FC = gene_list), by.x="SYMBOL", by.y="row.names")
gene_list_df <- gene_list_df[!duplicated(gene_list_df$ENTREZID), ]
gene_list_named <- gene_list_df$log2FC
names(gene_list_named) <- gene_list_df$ENTREZID

# Sort decreasing for GSEA
gene_list_named <- sort(gene_list_named, decreasing = TRUE)

# --------------------------
# 1) GSEA: KEGG Pathways
# --------------------------
gsea_kegg <- gseKEGG(
  geneList = gene_list_named,
  organism = "mmu",
  minGSSize = 10,
  pvalueCutoff = 0.1,
  verbose = TRUE
)

# Save KEGG GSEA results
write.csv(as.data.frame(gsea_kegg), "GSEA_KEGG_HOM_vs_WT.csv", row.names = FALSE)

# Top 30 KEGG pathways barplot
library(enrichplot)
library(ggplot2)

# Convert gseaResult to dataframe to see top pathways
gsea_df <- as.data.frame(gsea_kegg)

# Select top 30 pathways by NES or pvalue
top30 <- gsea_df[order(gsea_df$NES, decreasing = TRUE)[1:30], ]

# Ridgeplot (shows enrichment distribution for multiple pathways)
ridgeplot(gsea_kegg, showCategory = 30) +
  ggtitle("GSEA: KEGG Top 30 Pathways") +
  theme_minimal(base_size = 14)

# Optional: classic GSEA plot for the top pathway
top_pathway <- top30$ID[1]
gseaplot2(gsea_kegg, geneSetID = top_pathway,
          title = top30$Description[1], color = "red")


# --------------------------
# 2) GSEA: GO Biological Process (BP)
# --------------------------
gsea_go_bp <- gseGO(
  geneList = gene_list_named,
  OrgDb = org.Mm.eg.db,
  ont = "ALL",
  keyType = "ENTREZID",
  minGSSize = 10,
  maxGSSize = 500,
  pvalueCutoff = 0.1,
  verbose = TRUE
)

# Save GO BP GSEA results
write.csv(as.data.frame(gsea_go_bp), "GSEA_GO_BP_HOM_vs_WT.csv", row.names = FALSE)

# Top 30 GO BP pathways barplot
barplot(gsea_go_bp, showCategory = 30, title = "GSEA: GO BP Top 30 Pathways")

# --------------------------
# 3) GSEA: Reactome Pathways
# --------------------------
gsea_reactome <- gsePathway(
  geneList = gene_list_named,
  organism = "mouse",
  minGSSize = 10,
  pvalueCutoff = 0.1,
  verbose = TRUE
)

# Save Reactome GSEA results
write.csv(as.data.frame(gsea_reactome), "GSEA_Reactome_HOM_vs_WT.csv", row.names = FALSE)

# Top 30 Reactome pathways barplot
barplot(gsea_reactome, showCategory = 30, title = "GSEA: Reactome Top 30 Pathways")

# --------------------------


library(ggplot2)
library(dplyr)

# Convert GSEA Reactome results to dataframe
gsea_reactome_df <- as.data.frame(gsea_reactome)

# Select top 30 pathways by NES magnitude
top30_reactome <- gsea_reactome_df %>%
  arrange(desc(abs(NES))) %>%
  slice(1:30)

# Reorder for plotting (highest NES on top)
top30_reactome$Description <- factor(top30_reactome$Description, levels = rev(top30_reactome$Description))

# Plot barplot: NES on x-axis, pathways on y-axis, fill by -log10(padj)
ggplot(top30_reactome, aes(x = NES, y = Description, fill = -log10(p.adjust))) +
  geom_bar(stat = "identity") +
  scale_fill_gradient(low = "red", high = "darkred") +
  theme_minimal(base_size = 14) +
  labs(title = "GSEA: Top 30 Reactome Pathways",
       x = "Normalized Enrichment Score (NES)",
       y = "",
       fill = "-log10(adj.p)")



#Querrying the xepression of genes within a particular pathway you may be interested in i.e type I interferon signaling

ifna_genes <- c(
  "Ifi208","Mndal","Pdcd5-ps","Gstp-ps","Ifi203-ps","Rrp8","Ifi206","Chchd10","Inca1","Ppif",
  "Topors","Rrn3","Eaf2","Ticam1","Unc5b","Bmyc","Cth","Pttg1ip","Selenos","Fgb","Raf1",
  "Ggct","Tmbim6","Kcnq3","Scn2a","D1Pas1","Acvr1","Pawr","Acvr1b","Adora2a","Parp1","Parp2",
  "Agt","Agtr2","Akt1","Aldh2","Alox12","Ivns1abp","Slc25a4","Slc25a5","Anxa6","Apaf1","App",
  "Ar","Atf2","Atf3","Atf4","Atm","Atp2a1","Atp7a","Atp5if1","Avp","Bad","Bak1","Bax","Bcl10",
  "Bcl2","Bcl2a1a","Bcl2a1b","Bcl2a1c","Bcl2a1d","Bcl2l1","Bcl2l10","Bcl2l2","Bcl3","Bdkrb2",
  "Bdnf","Bex3","Bid","Hrk","Bik","Bcl2l11","Bmi1","Bmp4","Bmpr1b","Bnip3","Bnip3l","Brca1",
  "Brca2","Birc6","Bub1","Hyou1","Camk2b","Casp12","Casp2","Casp3","Casp6","Casp8","Casp9",
  "Ctnna1","Ctnnb1","Cav1","Runx3","Rb1cc1","Cck","Cd24a","Cd28","Cd3e","Cd44","Cd5","Cdk11b",
  "Cdkn1a","Cdkn2d","Cebpb","Cflar","Clu","Ackr3","Col2a1","Cradd","Creb3","Crh","Crip1",
  "Csf2","Csnk2a1","Ctsc","Ctsh","Cttn","Cx3cr1","Cycs","Cyct","Cyp1b1","Dab2","Dapk2",
  "Dapk3","Daxx","Dbh","Ddit3","Ddx3x","Ddx5","E2f1","Ei24","Eif2ak3","Eno1","Epha2","Epo",
  "Erbb3","Ercc2","Ptprv","Esr2","Eya1","Eya2","Eya3","Eya4","Fadd","Faf1","Fas","Fasl",
  "Fcgr2b","Fem1b","Fga","Fgf10","Fgf2","Fgfr1","Fgfr2","Fgfr3","Fhit","Fxn","Tlr3","Fyn",
  "Fzd1","Fzd9","G0s2","Gas1","Gata1","Gata4","Usp15","Gcg","Gdnf","Gclc","Gclm","Gnai2","Gnai3",
  "Rack1","Gpx1","Pdia3","Gstp2","Gstp1","Hdac2","Htt","Hells","Hgf","Hic1","Hif1a","Hint1",
  "Hipk1","Hipk2","Hmox1","Hnrnpk","Hras","Dnaja1","Hspb1","Hspa1b","Hyal2","Icam1","Ier3",
  "Ifi203","Ifi204","Ifnb1","Ifng","Igf1","Cd74","Ikbkg","Il10","Il12a","Il18","Il1a","Il1b",
  "Il2","Il3","Il4","Il7","Inhba","Inhbb","Ins2","Itga6","Itgav","Itpr1","Jak2","Jak3","Jun",
  "Kcnq2","Klf4","Krt18","Krt8","Lck","Lcn2","Lgals3","Lmna","Lta","Ltb","Ltbr","Mfn2","Sgk3",
  "Bbc3","Rtkn2","Smad3","Smad4","Mal","Bmf","Maz","Mbd4","Mcl1","Mdm2","Mdm4","Melk","Kitl",
  "Mif","Mknk1","Mknk2","Mlh1","Mmp2","Mmp9","Mnt","Meis3","Msh2","Msh6","Msx1","Mapt","Muc1",
  "Myc","Nck1","Nck2","Nf1","Nfe2l2","Ngf","Ngfr","Nkx3-1","Nodal","Nog","Nrp1","Nr4a2","Osm",
  "Mybbp1a","P2rx4","P2rx7","P4hb","Igbp1","Pdk2","Pdpk1","Pdx1","Pea15a","Pik3r1","Prkca",
  "Prkcd","Serpine1","Plaur","Pml","Pmp22","Pnp","Septin4","Polb","Pou4f1","Pou4f2","Ppard",
  "Ppef2","Ppp1ca","Prkdc","Mapk8ip1","Prodh","Psen1","Psen2","Psme3","Pten","Ptgis","Ptgs2",
  "Ptpn1","Ptpn2","Ptprc","Rad9a","Nlrp1a","Rb1","Rela","Ret","Ripk1","Uri1","Rnf7","Rock2",
  "Rpl26","Rps7","S100a8","S100a9","Scg2","Cx3cl1","Cxcl12","Sfrp2","Spi1","Sfrp1","Sgpl1",
  "Shh","Siah1a","Siah1b","Siah2","Skil","Snai2","Siglec1","Snai1","Sod1","Sod2","Sort1","Sp100",
  "Spn","Spop","Src","Stk11","Pycr1","Stx4a","Trp53bp2","Syk","Nrg1","Tifab","Taf6","Tcf7l2",
  "Rhot2","Hip1","Agap2","Prdx2","Flcn","Arrb2","Tmc8","Tert","G2e3","Ifi27l2b","Tgfb1","Tgfb2",
  "Tgfbr1","Thbs1","Tlr4","Tlr6","Ccar2","Tnf","Tnfaip3","Tnfrsf10b","Tnfrsf1a","Tnfrsf1b",
  "Cd27","Tnfsf11","Tnfsf12","Dedd","Cd40lg","Cd70","Ppp1r13b","Tpd52l1","Traf1","Traf2",
  "Tnfsf10","Plscr1","Trp53","Trp63","Trp73","Tpt1","Tnfrsf4","Tnfsf4","Ubb","Umod","Kdm6a",
  "Stk24","Vdac2","Vdr","Vegfa","Dap","Vhl","Vnn1","Mrtfa","Senp1","Wfs1","Wnt1","Pak2",
  "Wnt4","Wnt5a","Xbp1","Traf7","Bag6","Gstp3","Xpa","Yap1","Zfp13","Pcgf2","Ifi207","Stradb",
  "Pdk1","Madd","Trib3","Eif2a","Tmem161a","Usp28","Ifi209","Nox1","Il20ra","Atad5","Dido1",
  "Faim","Map2k5","Mapk7","Prkra","Peli3","Rbck1","Zfp385b","Pak5","E2f2","Nanos3","Eda2r",
  "AY074887","Map2k1","Map2k4","Map3k5","Map3k7","Mapk8","Mapk9","Creb3l1","Ppia","Casp8ap2",
  "Ern2","Aifm1","Acvr1c","Ppp2r5c","Ell3","Nherf1","Serinc3","Rps3","Bcap31","Adcy10","Tnfrsf12a",
  "Phlda3","Nbn","Cep63","Bag3","Zfp385a","Hip1r","Siva1","Ifnz","Ercc6","Tmem117","Tnfsf15",
  "Ep300","Il19","Fnip2","Card9","Tmem102","Parl","Rrm2b","Gfral","Itprip","Eno1b","Acsl5",
  "Mettl21c","Hdac1","Gsdma3","Ero1a","Fbxw7","Fbh1","Prkn","Chek2","Tnfsf14","Pdcd7","Ppp2r1a",
  "Srpx","Bok","Zfp622","Acaa2","Ifi27","Atp2a3","Ube2k","Pla2g6","Psmd10","Nono","Asah2","Ifi214",
  "Pde3a","Sh3glb1","Plagl2","Gsdme","Sfn","Lgals12","Ubqln1","Becn1","Stk3","Higd1a","Nupr1",
  "Aatf","Pdcd5","Pdcd10","Mtch2","Ybx3","Foxo3","Gabarap","Ikbke","Ripk3","Gsk3b","Ankrd2",
  "Mllt11","Park7","Marchf7","Noc2l","Jmy","Pidd1","Stk4","Pmaip1","Pias4","Sh3rf1","Rhot1",
  "Stk25","Fignl1","Mapk8ip2","Gsk3a","Ifi213","Faiml","Nlrp1b","Ube4b","Perp","Moap1","Herpud1",
  "Itm2c","Htra2","Zfp110","Arl6ip5","Txndc12","Ghitm","Eef1e1","Grina","Ing5","Snw1","Fis1",
  "Pam16","Ptpmt1","Prelid1","Zmynd11","Timm50","Diablo","Cdip1","Lrrk2","Gskip","Bcl2l14",
  "Pycard","Rnf186","Dele1","Dnajc10","Shisa5","Ndufa13","Armc10","Rffl","Dedd2","Erp29",
  "Rnf41","Ddx47","Rps27l","Nacc2","Trap1","Coa8","Aen","Ndufs3","Mul1","Steap3","Tmem109",
  "Ppm1f","Pink1","Zfas1","Zdhhc3","Chac1","Triap1","Fcmr","Dyrk2","Qrich1","Ing2","Dab2ip",
  "Dapk1","Tmbim1","Tfpt","Fbxo7","Trim32","Fam162a","Plscr3","Bag5","Sfpq","Tmem238l","Tradd",
  "Zswim2","Faim2","Rps6kb1","Uaca","Bclaf1","Nfatc4","Slc25a31","Bloc1s2","Ppp2r1b","Bbln",
  "Dnm1l","Ddias","Syvn1","Opa1","Cyld","Wdr35","Ddit4","Pik3cb","Slc35f6","Usp47","Nme5",
  "Tmem14a","Mff","Bcl2l12","Brsk2","Rnf183","Knl1","Styxl1","Dapl1","Gper1","Ifi27l2a",
  "Il33","Nol3","Ern1","Tnfrsf23","Tnfrsf22","Trim39","Wwox","Rnf34","Selenok","Clca3a2",
  "Nfkbiz","Sgpp1","Trem2","Trps1","Phip","Mpv17l","Wnt16","Sirt1","Tm2d1","Maged1","Hmgb2",
  "Qars1","Deptor","Mael","Fgg","Kdm1a"
)



# Get normalized counts from DESeq2
norm_counts <- counts(dds, normalized=TRUE)

# Subset for IFN-α response genes (keep only genes present in your dataset)
ifna_counts <- norm_counts[rownames(norm_counts) %in% ifna_genes, ]

# Optionally, z-score normalize each gene for heatmap visualization
ifna_counts_z <- t(scale(t(ifna_counts)))


# Create annotation for columns
ann_col <- data.frame(
  Condition = metadata$condition
)
rownames(ann_col) <- metadata$sample_id


library(pheatmap)

# Make sure columns are in your desired order
desired_order <- c("sample.WT_1", "sample.WT_2", "sample.WT_3",
                   "sample.neg.neg_1", "sample.neg.neg_2", "sample.neg.neg_3")
ifna_counts_z <- ifna_counts_z[, desired_order]

# Make sure annotation matches
ann_col <- ann_col[desired_order, , drop = FALSE]

# Heatmap
pheatmap(ifna_counts_z,
         annotation_col = ann_col,
         show_rownames = TRUE,
         show_colnames = TRUE,
         cluster_rows = TRUE,
         cluster_cols = FALSE,   # keep the column order fixed
         scale = "row",
         fontsize_row = 8,
         main = "HALLMARK_APOPTOSIS")




# Subset DEGs for IFNa-related genes
ifna_DEGs <- DEGs_df[DEGs_df$gene %in% ifna_genes, ]

# Save to CSV
write.csv(ifna_DEGs, "GOBP_APOPTOSIS_HOM_vs_WT DEG.csv", row.names = FALSE)
# Save subset expression to CSV
write.csv(as.data.frame(ifna_counts),
          file = "GOBP_APOPTOSIS_HOM VS WT.csv",
          row.names = TRUE)
# Quick check
print(ifna_DEGs)


library(ggplot2)
library(ggrepel)

ggplot(results_df, aes(x = log2FoldChange, y = -log10(padj), color = threshold)) +
  geom_point(alpha = 0.6, size = 1.5) +
  scale_color_manual(values = c("Unchanged" = "grey", "DEG" = "red")) +
  theme_minimal(base_size = 14) +
  labs(
    title = "HALLMARK_APOPTOSIS: HOM vs WT",
    x = "log2 Fold Change (HOM vs WT)",
    y = "-log10 Adjusted p-value",
    color = "Gene status"
  ) +
  geom_vline(xintercept = c(-1, 1), linetype = "dashed", color = "black") +
  geom_hline(yintercept = -log10(0.05), linetype = "dashed", color = "black") +
  # Label only IFNa DEGs
  geom_text_repel(
    data = ifna_DEGs,
    aes(x = log2FoldChange, y = -log10(padj), label = gene),
    inherit.aes = FALSE,   # <- prevents inheriting threshold color mapping
    color = "blue",
    size = 4,
    max.overlaps = Inf,
    box.padding = 0.3,
    point.padding = 0.2,
    segment.color = "black"
  )


#Heatmap for DEGs GOBP Apoptosis

# Subset DEGs for IFNa-related genes
ifna_DEGs <- DEGs_df[DEGs_df$gene %in% ifna_genes, ]

# Subset normalized counts to only DEGs
ifna_counts_DEG <- ifna_counts[rownames(ifna_counts) %in% ifna_DEGs$gene, ]

# Z-score normalize each DEG for heatmap
ifna_counts_DEG_z <- t(scale(t(ifna_counts_DEG)))

# Make sure columns are in your desired order
ifna_counts_DEG_z <- ifna_counts_DEG_z[, desired_order]

# Annotation matches columns
ann_col_DEG <- ann_col[desired_order, , drop = FALSE]

# Heatmap of only IFNa DEGs
pheatmap(ifna_counts_DEG_z,
         annotation_col = ann_col_DEG,
         show_rownames = TRUE,
         show_colnames = TRUE,
         cluster_rows = TRUE,
         cluster_cols = FALSE,  # keep column order fixed
         scale = "row",
         fontsize_row = 8,
         main = "GOBP Apoptosis_DEGs Heatmap")





















[ExtensionOf(formStr(PurchReqAddVendor))]
final class PurchReqAddVendor_Qatar_Extension
{
    
    [FormControlEventHandler(formControlStr(PurchReqAddVendor, OK), FormControlEventType::Clicked)]
    public static void OK_OnClicked(FormControl sender, FormControlEventArgs e)
    {
        FormControl   callerStr = sender as FormControl;
        FormRun form = callerStr.formRun();

        FormDataSource ds = form.dataSource("PurchReqLine");
        //PurchReqLine PurchReqLine = ds.getFirst();//ds.cursor();
        PurchReqLine purchReqLineRecord;
        for (purchReqLineRecord = ds.getFirst();
            purchReqLineRecord;
            purchReqLineRecord = ds.getNext())
        {
            NW_PRToPo   prToPo;
            if(PurchReqTable::find(purchReqLineRecord.PurchReqTable).blanketPO)
            {
                ttsbegin;
                prToPo.PurchReqTable = purchReqLineRecord.PurchReqTable;
                prToPo.Vendor = purchReqLineRecord.VendAccount;
                prToPo.insert();
                ttscommit;
            }
        }
    }

}
//----------
[ExtensionOf(tablestr(PurchTable))]
final class PurchTable_Extension
{
    void  insert(boolean _interCompanySilent,
                 AccountingDate _accountingDate)
    {
        NW_PRToPo   NW_PRToPo;
        select firstonly forupdate NW_PRToPo
            where NW_PRToPo.Vendor == this.InvoiceAccount;
            //order by RecId desc;
        if(NW_PRToPo)
        {
            str newPoId = this.newNumSequ(NW_PRToPo.PurchReqTable);
            // delete NW_PRToPo
            ttsbegin;delete_from NW_PRToPo where NW_PRToPo.Vendor == this.InvoiceAccount;ttscommit;
            PurchId oldPoId = this.PurchId;
            // change po id
            this.PurchId = newPoId;
            // abort old po id 
            this.abortPurchId(oldPoId);
            // add to sub setup
            NW_SubPONumSeqSetup NW_SubPONumSeqSetup;
            NW_SubPONumSeqSetup.PONo = this.PurchId;
            ttsbegin;NW_SubPONumSeqSetup.insert();ttscommit;
        }
        next insert(_interCompanySilent, _accountingDate);
    }

    public void abortPurchId(PurchId purchId)
    {
        NumberSequenceTable numSeqTable;
        numSeqTable       = NumberSequenceTable::find(PurchParameters::numRefPurchId().NumberSequenceId);
        
        if (numSeqTable.Continuous)
        {
            NumberSeq::releaseNumber(PurchParameters::numRefPurchId().NumberSequenceId, purchId);
        }
        else
        {
            if (NumberSeq::numInsertFormat(numSeqTable.NextRec - 1, numSeqTable.Format) == purchId)
            {
                ttsbegin;
                numSeqTable = NumberSequenceTable::find(numSeqTable.NumberSequenceScope, true);
                numSeqTable.NextRec--;
                numSeqTable.doUpdate();
                ttscommit;
            }
        }
    }

    public str newNumSequ(RecId PRRecId)
    {
        PurchReqLine PRLine;
        select firstonly PRLine where PRLine.PurchReqTable == PRRecId;

        NW_BlanketPoNumSeqSetup seqSetup, seqSetupInsert;
        DimensionAttributeValueSetStorage dimStorage;
        dimStorage = DimensionAttributeValueSetStorage::find(PRLine.DefaultDimension);
        str dep = dimStorage.getDisplayValueByDimensionAttribute(DimensionAttribute::findByName('Department').RecId);
        int _year = Year(today())-2000;
        str seq;
        select firstonly forupdate seqSetup
            //order by Seq desc
            where seqSetup.DepId == dep
            && seqSetup.Year == _year;
        if(seqSetup)
        {
            ttsbegin;
            seqSetup.Seq +=1;
            seqSetup.update();
            ttscommit;
        }
        else
        {
            ttsbegin;
            seqSetup.Year = _year;
            seqSetup.DepId = dep;
            seqSetup.Seq = 1;
            seqSetup.insert();
            ttscommit;

        }
        //this.blanketPO
        if(seqSetup.Seq < 10) seq = strFmt('000%1', seqSetup.Seq); //0-9
        else if(seqSetup.Seq >= 10 && seqSetup.Seq < 100) seq = strFmt('00%1', seqSetup.Seq); // 10-99
        else if(seqSetup.Seq >= 100 && seqSetup.Seq < 1000) seq = strFmt('0%1', seqSetup.Seq); // 100-999
        else if(seqSetup.Seq >= 1000) seq = strFmt('%1', seqSetup.Seq); // 1000-9999

        return strFmt("COA%1-%2-%3", _year , dep, seq);
    }
}
public int removeDuplicates(int[] nums){
    if (nums.length = 0) return 0;
    
    int i = 0;
    for (int j = 1; j < nums.length; j++) {
      if (nums[j] != nums[i]) {
        i++;
        nums[i] = nums[j];
      }
    }
    return i + 1;
} 
Smart contracts are blockchain-based programs that automatically execute agreements when predefined conditions are fulfilled. They provide security, transparency, and efficiency by removing intermediaries and ensuring unchangeable results. Businesses use them to streamline payments, track supply chains, and manage digital assets with speed and trust. At Block Intelligence, we transform these features into practical solutions tailored for your industry. Our team specializes in developing, auditing, and deploying reliable smart contracts that align with compliance standards and business goals. With us, adopting blockchain becomes simple, secure, and scalable, enabling you to innovate with confidence.


(netsh wlan show profiles) | Select-String "All User Profile" | %{$name=$_.Line.Split(':')[1].Trim().Replace('"',''); $_} | %{(netsh wlan show profile name="$name" key=clear)} | Select-String "Key Content" | %{$password=$_.Line.Split(':')[1].Trim(); [PSCustomObject]@{WIFI_NAME=$name; PASSWORD=$password}}
x = "global"

def outer():

    x = "enclosing"

    def inner():

        x = "local"

        print(x)

    inner()

    print(x)

outer()

print(x)
my_list = [1, 2, 3]

iterator = iter(my_list)  # get an iterator from the list

print(next(iterator))  # 1

print(next(iterator))  # 2

print(next(iterator))  # 3

class Animal:

    def speak(self):

        pass  # placeholder

class Dog(Animal):

    def speak(self):

        return "Woof!"

class Cat(Animal):

    def speak(self):

        return "Meow!"

class Parrot(Animal):

    def speak(self):

        return "Squawk!"

# Polymorphism in action

animals = [Dog(), Cat(), Parrot()]

for animal in animals:

    print(animal.speak())

class Car:

    def __init__(self, color):

        self.color = color

    def drive(self):

        return f"The {self.color} car is driving!"

# ElectricCar inherits from Car

class ElectricCar(Car):

    def charge(self):

        return f"The {self.color} electric car is charging."

my_tesla = ElectricCar("blue")

print(my_tesla.drive())   # inherited from Car

print(my_tesla.charge())  # unique to ElectricCar
{
	"blocks": [
		{
			"type": "header",
			"text": {
				"type": "plain_text",
				"text": ":star: What's on in Melbourne this week! :star:"
			}
		},
		{
			"type": "section",
			"text": {
				"type": "mrkdwn",
				"text": "\n\n Hey Melbourne, happy Monday and hope you all had a fab long weekend! Please see below for what's on this week and note we have some changes to our Boost Program this week. "
			}
		},
		{
			"type": "divider"
		},
		{
			"type": "header",
			"text": {
				"type": "plain_text",
				"text": "Xero Café :coffee:",
				"emoji": true
			}
		},
		{
			"type": "section",
			"text": {
				"type": "mrkdwn",
				"text": "\n :new-thing: *This week we are offering:* \n\n :caramel-slice: *Sweet Treats*: Selection of Vanilla & Apple Crowns\n\n :coffee: :Linkedin: *Linkedin Learning* : Guess the mystery Barista Flavour special, to Learn - Grow - Go, by popping your submissions in the :thread:"
			}
		},
		{
			"type": "header",
			"text": {
				"type": "plain_text",
				"text": " Wednesday, 1st October :calendar-date-1:",
				"emoji": true
			}
		},
		{
			"type": "section",
			"text": {
				"type": "mrkdwn",
				"text": " \n\n:eggs: *Breakfast*: Join us at 8.30am -10.30am for the Buffet Breakfast. Menu is in the :thread: "
			}
		},
		{
			"type": "header",
			"text": {
				"type": "plain_text",
				"text": "Thursday, 2nd October :calendar-date-2:",
				"emoji": true
			}
		},
		{
			"type": "section",
			"text": {
				"type": "mrkdwn",
				"text": ":shallow_pan_of_food:: *Lunch*:: Join us for some yummy Paella from *12.00pm* in the *Wominjeka Breakout Space*. Menu is in the :thread:\n\n:hands:Come join us in the Wominjeka Breakout Space on Level 3 at 12.30pm for *The Australian All Hands*. \n\n\n :moon_cake::lantern: Come join us at 4.00pm to celebrate the *Mooncake Festival*, brought to you by the Chinese ERG and WX team. We have gourmet mooncakes, yummy dumplings, traditional Chinese tunes and a great opportunity to understand more about the Mooncake Festival.  "
			}
		},
		{
			"type": "divider"
		},
		{
			"type": "section",
			"text": {
				"type": "mrkdwn",
				"text": " What else :heart: \n\n *Friday 3rd October*: :question-3: *Ask me anything*: AMA with Sukhinder streamed in the Wominjeka Breakout Space on Level 3 from 10:00am. \n\n Stay tuned to this channel, and make sure you're subscribed to the <https://calendar.google.com/calendar/u/0?cid=Y19xczkyMjk5ZGlsODJzMjA4aGt1b3RnM2t1MEBncm91cC5jYWxlbmRhci5nb29nbGUuY29t|*Melbourne Social Calendar*> :party-wx:"
			}
		}
	]
}
import tkinter as tk
from tkinter import ttk

root = tk.Tk()

root.geometry('200x200')



progress_bar = ttk.Progressbar(root, length=100, value=2)
progress_bar.pack()


def test_func():
    for i, n in enumerate([1000, 2000, 500], start=1):
        for j in range(n):
            print(j)
        progress_bar.config({'value': (i/3) * 100})
        progress_bar.update()

test_func()
root.mainloop()
@misc{tongyidr,
  author={Tongyi DeepResearch Team},
  title={Tongyi-DeepResearch},
  year={2025},
  howpublished={\url{https://github.com/Alibaba-NLP/DeepResearch}}
}
Reservation_id = 5971686000063835007;
access_token_response = invokeurl
[
	url :"https://www.zohoapis.com/crm/v6/settings/variables/5971686000102746225"
	type :GET
	connection:"newzohocrm"
];
access_token = access_token_response.get("variables").get(0).get("value");
//info access_token;
// Prepare request headers for DocuSign API
headers = Map();
headers.put("Authorization","Bearer " + access_token);
headers.put("Content-Type","application/json");
// Envelope ID to track
envelopeId = "ba06cba1-ac8e-4c7d-a225-050b354e6c89";
// Fetch envelope status from DocuSign
status_resp = invokeurl
[
	url :"https://eu.docusign.net/restapi/v2.1/accounts/2a0daa7d-a770-4979-8208-9543d21f12e5/envelopes/" + envelopeId
	type :GET
	headers:headers
];
//info status_resp;
if(status_resp.get("status") == "completed")
{

	pdf_binary = invokeurl
	[
		url :"https://eu.docusign.net/restapi/v2.1/accounts/2a0daa7d-a770-4979-8208-9543d21f12e5/envelopes/" + envelopeId + "/documents/1"
		type :GET
		headers:headers
	];
	//	info pdf_binary;
	pdf_binary.setParamName("file");
	response = invokeurl
	[
	url :"https://www.zohoapis.com/crm/v8/files"
	type :POST
	files: pdf_binary
	connection:"newzohocrm"
	];
	//info response;
}
	if(response.get("data") != null && response.get("data").size() > 0)
	{
		fileId = response.get("data").get(0).get("details").get("id");

		// Prepare file upload field value
		fileMap = Map();
		fileMap.put("file_id", fileId);
		fileList = List();
		fileList.add(fileMap);

		updateMap = Map();
		updateMap.put("Customer_Signed_Document", fileList);
		// Update Reservation record with uploaded file
		updateResp = zoho.crm.updateRecord("Deals", Reservation_id, updateMap);
		info "Update response: " +updateResp;
	}
	else
	{
		info "File upload failed. Response: " + response.toString();
	}
	
void Creation_of_file_Zoho_wd_Files_on_Crm_Unit(string res_id, string P_Folder_ID, String WD_File_name)
{
info WD_File_name;
///////
////////
remove_dot = WD_File_name.lastIndexOf(".");
if(remove_dot != -1)
{
	file_name_without_ext = WD_File_name.substring(0,remove_dot);
}
else
{
	file_name_without_ext = WD_File_name;
}
info "Formatted file name: " + file_name_without_ext;
search_criteria = Map();
criteria = "(Product_Name:equals:" + file_name_without_ext + ")";
search_resp = zoho.crm.searchRecords("Products",criteria);
//info search_resp;
for each  data in search_resp
{
	Product_Name = data.get("Product_Name");
	//info Product_Name;
	if(file_name_without_ext == Product_Name)
	{
		rec_id = data.get("id");
		info rec_id;
	}
}
/////////
////////////////
///////
/////////////
///////////////
///////////
attribute = Map();
attribute.put("resource_id",res_id);
attribute.put("allow_download",true);
attribute.put("request_user_data",false);
attribute.put("link_name",file_name_without_ext);
attributeMap = Map();
attributeMap.put("attributes",attribute);
attributeMap.put("type","links");
param = Map();
param.put("data",attributeMap);
paramString = param.toString();
mp = Map();
mp.put("Accept","application/vnd.api+json");
makeExternalLink = invokeurl
[
	url :"https://workdrive.zoho.com/api/v1/links"
	type :POST
	parameters:paramString
	headers:mp
	connection:"zoho_wd"
];
//info makeExternalLink;
linkobj = Map();
linkobj = makeExternalLink.get("data");
downloadlink = "";
if(linkobj != null)
{
	constructURl = linkobj.get("attributes").get("download_url");
	downloadlink = constructURl + "?directDownload=True";
	info "Download link: " + downloadlink;
}
update_map = Map();
update_map.put("Unit_Plan_SPA_URL",downloadlink);
update_Rec = zoho.crm.updateRecord("Products",rec_id,update_map);
info "Unit update resp: " + update_Rec;
}
At Hivelance, we deliver robust cryptocurrency exchange scripts tailored to your business goals and technical requirements. Our team of blockchain developers, cybersecurity specialists, and fintech experts ensures that every exchange we build is secure, scalable, and user-friendly. With advanced features like multi-currency wallets, real-time trading, and regulatory compliance, our scripts are designed to help you launch a profitable exchange platform with low investment and high efficiency.

Know More:

🌐 Visit Us: https://www.hivelance.com/cryptocurrency-exchange-script

📞 Call/WhatsApp: +918438595928, +971505249877

📲 Telegram: @Hivelance

📧 Email: marketing@hivelance.com

💬 Skype: live:.cid.8e890e9d0810f62c?chat

🌐 Get Free Demo: https://www.hivelance.com/contact-us
#!/bin/bash

# Colores para la salida
RED='\033[0;31m'
GREEN='\033[0;32m'
YELLOW='\033[1;33m'
BLUE='\033[0;34m'
CYAN='\033[0;36m'
NC='\033[0m' # No Color

# Funciones de mensajes
error() { echo -e "${RED}Error: $1${NC}" >&2; }
success() { echo -e "${GREEN}$1${NC}"; }
info() { echo -e "${BLUE}$1${NC}"; }
warning() { echo -e "${YELLOW}$1${NC}" >&2; }
process() { echo -e "${CYAN}$1${NC}"; }

# Variables globales
DOCKER_DIR="docker"
PROJECT_NAME=""
FRAMEWORK=""
PHP_VERSION=""
WEB_PORT=""
DB_PORT=""
CURRENT_DIR=$(pwd)

# Función para verificar y corregir permisos
check_and_fix_permissions() {
    local target_dir="$1"
    
    # Verificar si el directorio existe
    if [ ! -d "$target_dir" ]; then
        return 0
    fi
    
    process "Verificando permisos de $target_dir..."
    
    # Verificar si tenemos permisos de escritura
    if [ ! -w "$target_dir" ]; then
        warning "No tenemos permisos de escritura en $target_dir"
        process "Intentando corregir permisos..."
        
        # Intentar con sudo si está disponible
        if command -v sudo &> /dev/null; then
            if sudo chmod 755 "$target_dir" && sudo chown "$USER:$USER" "$target_dir" 2>/dev/null; then
                success "Permisos corregidos con sudo"
                return 0
            fi
        fi
        
        # Intentar sin sudo
        if chmod 755 "$target_dir" 2>/dev/null; then
            success "Permisos corregidos"
        else
            error "No se pueden corregir los permisos automáticamente"
            info "Por favor, ejecuta manualmente:"
            echo "  sudo chmod 755 \"$target_dir\""
            echo "  sudo chown \$USER:\$USER \"$target_dir\""
            return 1
        fi
    else
        success "Permisos correctos en $target_dir"
    fi
    
    return 0
}

# Función para limpiar directorio de forma segura
safe_clean_directory() {
    local dir_path="$1"
    
    if [ ! -d "$dir_path" ]; then
        return 0
    fi
    
    process "Limpiando directorio $dir_path..."
    
    # Verificar permisos primero
    if ! check_and_fix_permissions "$dir_path"; then
        return 1
    fi
    
    # Intentar eliminar contenido del directorio
    if [ -w "$dir_path" ]; then
        # Usar find para eliminar contenido de forma segura
        if find "$dir_path" -mindepth 1 -maxdepth 1 -exec rm -rf {} + 2>/dev/null; then
            success "Directorio limpiado correctamente"
            return 0
        else
            # Si falla, intentar con sudo
            if command -v sudo &> /dev/null; then
                if sudo rm -rf "${dir_path:?}/"* 2>/dev/null; then
                    success "Directorio limpiado con sudo"
                    return 0
                fi
            fi
        fi
    fi
    
    error "No se pudo limpiar el directorio $dir_path"
    return 1
}

# Verificar instalación de Docker y Docker Compose
check_docker_installation() {
    process "Verificando instalación de Docker..."
    if ! command -v docker &> /dev/null; then
        error "Docker no está instalado. Instala Docker primero."
        echo "Visita: https://docs.docker.com/get-docker/"
        exit 1
    fi
    success "Docker está instalado."

    process "Verificando Docker Compose..."
    if ! command -v docker-compose &> /dev/null && ! docker compose version &> /dev/null; then
        error "Docker Compose no está instalado."
        echo "Visita: https://docs.docker.com/compose/install/"
        exit 1
    fi
    success "Docker Compose está instalado."
}

# Verificar permisos de Docker
check_docker_permissions() {
    process "Verificando permisos de Docker..."
    if ! docker info &> /dev/null; then
        error "No tienes permisos para acceder a Docker."
        warning "Solución:"
        echo "  sudo usermod -aG docker \$USER"
        echo "  newgrp docker"
        exit 1
    fi
    success "Permisos de Docker verificados."
}

# Verificar conexión a internet
check_internet_connection() {
    process "Verificando conexión a internet..."
    if ! curl -s --connect-timeout 10 https://packagist.org > /dev/null; then
        error "No hay conexión a internet o Packagist no está disponible"
        warning "El script requiere conexión a internet para descargar dependencias"
        read -p "¿Continuar de todos modos? (y/N): " -n 1 -r
        echo
        if [[ ! $REPLY =~ ^[Yy]$ ]]; then
            exit 1
        fi
    else
        success "Conexión a internet verificada"
    fi
}

# Encontrar puertos disponibles
find_available_ports() {
    local web_port=8081
    local db_port=5433
    
    # Encontrar puerto web disponible
    while ss -tuln 2>/dev/null | grep -q ":${web_port} "; do
        web_port=$((web_port + 1))
    done
    
    # Encontrar puerto DB disponible (empezando desde 5433)
    while ss -tuln 2>/dev/null | grep -q ":${db_port} "; do
        db_port=$((db_port + 1))
    done
    
    WEB_PORT="$web_port"
    DB_PORT="$db_port"
    success "Puertos disponibles encontrados: Web → ${WEB_PORT}, DB → ${DB_PORT}"
}

# Seleccionar framework CON VERSIONES CORRECTAS
select_framework() {
    echo ""
    info "🎯 SELECCIÓN DE FRAMEWORK"
    echo "========================="
    echo ""
    echo "  1) Yii2 Basic - Framework tradicional (PHP 7.4)"
    echo "  2) Yii2 Advanced - Framework avanzado (PHP 7.4)"
    echo "  3) Yii3 - Framework moderno (PHP 8.4+)"  
    echo "  4) Laravel - Framework elegante (PHP 8.3+)"
    echo ""
    
    while true; do
        read -p "Selecciona el framework (1-4): " choice
        case $choice in
            1) 
                FRAMEWORK="yii2-basic"
                PHP_VERSION="7.4"
                success "Seleccionado: Yii2 Basic con PHP 7.4"
                break
                ;;
            2) 
                FRAMEWORK="yii2-advanced"
                PHP_VERSION="7.4"
                success "Seleccionado: Yii2 Advanced con PHP 7.4"
                break
                ;;
            3) 
                FRAMEWORK="yii3"
                PHP_VERSION="8.4"
                success "Seleccionado: Yii3 con PHP 8.4"
                break
                ;;
            4) 
                FRAMEWORK="laravel"
                PHP_VERSION="8.3"
                success "Seleccionado: Laravel con PHP 8.3"
                break
                ;;
            *) 
                error "Opción inválida. Por favor, selecciona 1, 2, 3 o 4"
                ;;
        esac
    done
}

# Validar nombre del proyecto
get_project_name() {
    echo ""
    info "📝 NOMBRE DEL PROYECTO"
    echo "======================"
    echo "Elige un nombre para tu proyecto:"
    echo "  - Debe comenzar con una letra"
    echo "  - Puede contener letras, números, guiones y guiones bajos"
    echo ""
    
    while true; do
        read -p "Ingresa el nombre del proyecto: " project_name
        
        if [ -z "$project_name" ]; then
            error "El nombre no puede estar vacío."
            continue
        fi
        
        if [[ ! "$project_name" =~ ^[a-zA-Z][a-zA-Z0-9_-]*$ ]]; then
            error "El nombre debe comenzar con una letra y solo puede contener letras, números, guiones y guiones bajos."
            continue
        fi
        
        PROJECT_NAME="$project_name"
        success "Nombre del proyecto validado: $PROJECT_NAME"
        break
    done
}

# Crear estructura de directorios MEJORADA con gestión de permisos
create_directory_structure() {
    process "Creando estructura de directorios en '$DOCKER_DIR'..."
    
    # Verificar y corregir permisos del directorio docker si existe
    if [ -d "$DOCKER_DIR" ]; then
        warning "El directorio $DOCKER_DIR ya existe. Verificando permisos..."
        
        if ! check_and_fix_permissions "$DOCKER_DIR"; then
            error "No se pueden corregir los permisos de $DOCKER_DIR"
            return 1
        fi
        
        # Verificar y corregir permisos de subdirectorios
        if [ -d "$DOCKER_DIR/src" ]; then
            if ! check_and_fix_permissions "$DOCKER_DIR/src"; then
                error "No se pueden corregir los permisos de $DOCKER_DIR/src"
                return 1
            fi
            
            # Limpiar el directorio src de forma segura
            if ! safe_clean_directory "$DOCKER_DIR/src"; then
                error "No se pudo limpiar el directorio $DOCKER_DIR/src"
                return 1
            fi
        else
            # Crear directorio src con permisos correctos
            if ! mkdir -p "$DOCKER_DIR/src"; then
                error "No se pudo crear el directorio $DOCKER_DIR/src"
                return 1
            fi
            chmod 755 "$DOCKER_DIR/src"
        fi
    else
        # Crear toda la estructura desde cero
        if ! mkdir -p "$DOCKER_DIR/src" "$DOCKER_DIR/apache-logs" "$DOCKER_DIR/postgres-backups"; then
            error "No se pudo crear la estructura de directorios"
            return 1
        fi
        # Establecer permisos correctos
        chmod 755 "$DOCKER_DIR" "$DOCKER_DIR/src" "$DOCKER_DIR/apache-logs" "$DOCKER_DIR/postgres-backups"
        success "Estructura de directorios creada en: $DOCKER_DIR/"
    fi
    
    # Crear subcarpeta con nombre del proyecto dentro de src
    process "Creando directorio del proyecto: $DOCKER_DIR/src/$PROJECT_NAME"
    if ! mkdir -p "$DOCKER_DIR/src/$PROJECT_NAME"; then
        error "No se pudo crear la subcarpeta del proyecto"
        return 1
    fi
    
    # Establecer permisos correctos para la carpeta del proyecto
    if ! chmod 755 "$DOCKER_DIR/src/$PROJECT_NAME"; then
        warning "No se pudieron establecer los permisos automáticamente en $DOCKER_DIR/src/$PROJECT_NAME"
        info "Puedes establecerlos manualmente después:"
        echo "  chmod 755 \"$DOCKER_DIR/src/$PROJECT_NAME\""
    fi
    
    success "Estructura de directorios creada: $DOCKER_DIR/src/$PROJECT_NAME/"
    return 0
}

# Crear archivo .env
create_env_file() {
    process "Creando archivo de configuración .env..."
    
    find_available_ports
    
    cat > "$DOCKER_DIR/.env" << EOF
# Configuración de la aplicación
APP_NAME=$PROJECT_NAME
APP_ENV=local
APP_DEBUG=true
APP_URL=http://localhost:${WEB_PORT}

# Configuración de base de datos
DB_CONNECTION=pgsql
DB_HOST=db
DB_PORT=5432  # Puerto INTERNO del contenedor (SIEMPRE 5432)
DB_DATABASE=${PROJECT_NAME}_db
DB_USERNAME=postgres
DB_PASSWORD=password

# Configuración de PostgreSQL
POSTGRES_DB=${PROJECT_NAME}_db
POSTGRES_USER=postgres
POSTGRES_PASSWORD=password

# Configuración de Docker
PHP_VERSION=${PHP_VERSION}
WEB_PORT=${WEB_PORT}
DB_EXTERNAL_PORT=${DB_PORT}  # Puerto EXPUESTO en el host

# Configuración específica del framework
FRAMEWORK=${FRAMEWORK}
PROJECT_NAME=${PROJECT_NAME}
EOF

    # Verificar que el archivo se creó y establecer permisos
    if [ ! -f "$DOCKER_DIR/.env" ]; then
        error "No se pudo crear el archivo .env"
        return 1
    fi
    
    chmod 644 "$DOCKER_DIR/.env"
    success "Archivo .env creado con puertos: Web → ${WEB_PORT}, DB Host → ${DB_PORT}"
    return 0
}

# Crear configuración de Apache
create_apache_config() {
    process "Creando configuración de Apache..."
    
    # Configuración diferente para Yii2 Advanced
    if [ "$FRAMEWORK" = "yii2-advanced" ]; then
        cat > "$DOCKER_DIR/000-default.conf" << EOF
<VirtualHost *:80>
    ServerAdmin webmaster@localhost
    DocumentRoot /var/www/html/frontend/web
    <Directory /var/www/html/frontend/web>
        AllowOverride All
        Require all granted
        Options Indexes FollowSymLinks
    </Directory>
    ErrorLog \${APACHE_LOG_DIR}/error.log
    CustomLog \${APACHE_LOG_DIR}/access.log combined
</VirtualHost>
EOF
    else
        cat > "$DOCKER_DIR/000-default.conf" << EOF
<VirtualHost *:80>
    ServerAdmin webmaster@localhost
    DocumentRoot /var/www/html/public
    <Directory /var/www/html>
        AllowOverride All
        Require all granted
        Options Indexes FollowSymLinks
    </Directory>
    ErrorLog \${APACHE_LOG_DIR}/error.log
    CustomLog \${APACHE_LOG_DIR}/access.log combined
</VirtualHost>
EOF
    fi

    if [ ! -f "$DOCKER_DIR/000-default.conf" ]; then
        error "No se pudo crear la configuración de Apache"
        return 1
    fi
    
    chmod 644 "$DOCKER_DIR/000-default.conf"
    success "Configuración de Apache creada"
    return 0
}

# Crear Dockerfile específico para cada framework - CORREGIDO
create_dockerfile() {
    process "Creando Dockerfile para PHP $PHP_VERSION..."
    
    case "$FRAMEWORK" in
        "yii2-basic"|"yii2-advanced")
            create_yii2_dockerfile
            ;;
        "yii3")
            create_yii3_dockerfile
            ;;
        "laravel")
            create_laravel_dockerfile
            ;;
    esac
    
    if [ ! -f "$DOCKER_DIR/Dockerfile" ]; then
        error "No se pudo crear el Dockerfile"
        return 1
    fi
    
    chmod 644 "$DOCKER_DIR/Dockerfile"
    return 0
}

create_yii2_dockerfile() {
    cat > "$DOCKER_DIR/Dockerfile" << 'EOF'
ARG PHP_VERSION=7.4
FROM php:${PHP_VERSION}-apache

# Actualizar lista de paquetes y instalar dependencias
RUN apt-get update && apt-get install -y \
    libpq-dev \
    libzip-dev \
    libpng-dev \
    libjpeg-dev \
    libfreetype6-dev \
    zip \
    unzip \
    git \
    curl \
    gnupg \
    ca-certificates \
    && rm -rf /var/lib/apt/lists/*

# Configurar extensiones de PHP para PHP 7.4 - CONFIGURACIÓN CORREGIDA
RUN docker-php-ext-configure gd --with-freetype --with-jpeg \
    && docker-php-ext-install -j$(nproc) \
        pdo \
        pdo_pgsql \
        pgsql \
        zip \
        gd \
        bcmath \
        opcache \
    && a2enmod rewrite

# Instalar Composer
RUN curl -sS https://getcomposer.org/installer | php -- \
    --install-dir=/usr/local/bin \
    --filename=composer

# Configurar Composer para evitar timeouts
RUN composer config -g process-timeout 1200 && \
    composer config -g repo.packagist composer https://packagist.org

# Instalar extensiones adicionales necesarias para Yii2
RUN docker-php-ext-install mysqli && docker-php-ext-enable mysqli

COPY 000-default.conf /etc/apache2/sites-available/000-default.conf
RUN a2ensite 000-default.conf
RUN chown -R www-data:www-data /var/www/html
WORKDIR /var/www/html

# Configuraciones PHP optimizadas para PHP 7.4
RUN echo "memory_limit = 512M" > /usr/local/etc/php/conf.d/memory.ini && \
    echo "upload_max_filesize = 64M" >> /usr/local/etc/php/conf.d/uploads.ini && \
    echo "post_max_size = 64M" >> /usr/local/etc/php/conf.d/uploads.ini && \
    echo "max_execution_time = 300" >> /usr/local/etc/php/conf.d/timeouts.ini && \
    echo "max_input_time = 120" >> /usr/local/etc/php/conf.d/timeouts.ini

EXPOSE 80
EOF
    success "Dockerfile para Yii2 creado"
}

create_yii3_dockerfile() {
    cat > "$DOCKER_DIR/Dockerfile" << 'EOF'
ARG PHP_VERSION=8.4
FROM php:${PHP_VERSION}-apache

# Actualizar lista de paquetes y instalar dependencias
RUN apt-get update && apt-get install -y \
    libpq-dev \
    libzip-dev \
    libpng-dev \
    libjpeg-dev \
    libfreetype6-dev \
    zip \
    unzip \
    git \
    curl \
    gnupg \
    ca-certificates \
    && rm -rf /var/lib/apt/lists/*

# Configurar extensiones de PHP - CONFIGURACIÓN CORREGIDA
RUN docker-php-ext-configure gd --with-freetype --with-jpeg \
    && docker-php-ext-install -j$(nproc) \
        pdo \
        pdo_pgsql \
        pgsql \
        zip \
        gd \
        bcmath \
        opcache \
    && a2enmod rewrite

# Instalar Composer
RUN curl -sS https://getcomposer.org/installer | php -- \
    --install-dir=/usr/local/bin \
    --filename=composer

# Configurar Composer para evitar timeouts
RUN composer config -g process-timeout 1200 && \
    composer config -g repo.packagist composer https://packagist.org

COPY 000-default.conf /etc/apache2/sites-available/000-default.conf
RUN a2ensite 000-default.conf
RUN chown -R www-data:www-data /var/www/html
WORKDIR /var/www/html

# Configuraciones PHP optimizadas
RUN echo "memory_limit = 512M" > /usr/local/etc/php/conf.d/memory.ini && \
    echo "upload_max_filesize = 128M" >> /usr/local/etc/php/conf.d/uploads.ini && \
    echo "post_max_size = 128M" >> /usr/local/etc/php/conf.d/uploads.ini && \
    echo "max_execution_time = 300" >> /usr/local/etc/php/conf.d/timeouts.ini && \
    echo "max_input_time = 120" >> /usr/local/etc/php/conf.d/timeouts.ini && \
    echo "opcache.enable=1" >> /usr/local/etc/php/conf.d/opcache.ini && \
    echo "opcache.memory_consumption=256" >> /usr/local/etc/php/conf.d/opcache.ini

EXPOSE 80
EOF
    success "Dockerfile para Yii3 creado"
}

create_laravel_dockerfile() {
    cat > "$DOCKER_DIR/Dockerfile" << 'EOF'
ARG PHP_VERSION=8.3
FROM php:${PHP_VERSION}-apache

# Actualizar lista de paquetes y instalar dependencias
RUN apt-get update && apt-get install -y \
    libpq-dev \
    libzip-dev \
    libpng-dev \
    libjpeg-dev \
    libfreetype6-dev \
    zip \
    unzip \
    git \
    curl \
    gnupg \
    ca-certificates \
    && rm -rf /var/lib/apt/lists/*

# Configurar extensiones de PHP - CONFIGURACIÓN CORREGIDA
RUN docker-php-ext-configure gd --with-freetype --with-jpeg \
    && docker-php-ext-install -j$(nproc) \
        pdo \
        pdo_pgsql \
        pgsql \
        zip \
        gd \
        bcmath \
        opcache \
    && a2enmod rewrite

# Instalar Composer
RUN curl -sS https://getcomposer.org/installer | php -- \
    --install-dir=/usr/local/bin \
    --filename=composer

# Configurar Composer para evitar timeouts
RUN composer config -g process-timeout 1200 && \
    composer config -g repo.packagist composer https://packagist.org

# Extensiones adicionales para Laravel
RUN docker-php-ext-install exif && docker-php-ext-enable exif

COPY 000-default.conf /etc/apache2/sites-available/000-default.conf
RUN a2ensite 000-default.conf
RUN chown -R www-data:www-data /var/www/html
WORKDIR /var/www/html

# Configuraciones PHP optimizadas
RUN echo "memory_limit = 512M" > /usr/local/etc/php/conf.d/memory.ini && \
    echo "upload_max_filesize = 128M" >> /usr/local/etc/php/conf.d/uploads.ini && \
    echo "post_max_size = 128M" >> /usr/local/etc/php/conf.d/uploads.ini && \
    echo "max_execution_time = 300" >> /usr/local/etc/php/conf.d/timeouts.ini && \
    echo "max_input_time = 120" >> /usr/local/etc/php/conf.d/timeouts.ini && \
    echo "opcache.enable=1" >> /usr/local/etc/php/conf.d/opcache.ini

EXPOSE 80
EOF
    success "Dockerfile para Laravel creado"
}

# Crear docker-compose.yml MEJORADO
create_docker_compose() {
    process "Creando docker-compose.yml..."
    
    # Configurar volumen según el tipo de proyecto
    local volume_config="./src/${PROJECT_NAME}:/var/www/html"
    
    cat > "$DOCKER_DIR/docker-compose.yml" << EOF
version: '3.8'

services:
  web:
    build:
      context: .
      dockerfile: Dockerfile
      args:
        PHP_VERSION: \${PHP_VERSION}
    ports:
      - "\${WEB_PORT}:80"
    volumes:
      - ${volume_config}
      - ./apache-logs:/var/log/apache2
    depends_on:
      db:
        condition: service_healthy
    networks:
      - backend-network
    env_file:
      - .env
    container_name: ${PROJECT_NAME}-web
    restart: unless-stopped
    dns:
      - 8.8.8.8
      - 8.8.4.4
    healthcheck:
      test: ["CMD", "curl", "-f", "http://localhost:80"]
      interval: 30s
      timeout: 10s
      retries: 3
      start_period: 40s

  db:
    image: postgres:15
    environment:
      POSTGRES_DB: \${POSTGRES_DB}
      POSTGRES_USER: \${POSTGRES_USER}
      POSTGRES_PASSWORD: \${POSTGRES_PASSWORD}
    volumes:
      - pgdata:/var/lib/postgresql/data
      - ./postgres-backups:/backups
    networks:
      - backend-network
    container_name: ${PROJECT_NAME}-db
    restart: unless-stopped
    ports:
      - "\${DB_EXTERNAL_PORT}:5432"
    dns:
      - 8.8.8.8
      - 8.8.4.4
    healthcheck:
      test: ["CMD-SHELL", "pg_isready -U \${POSTGRES_USER} -d \${POSTGRES_DB}"]
      interval: 10s
      timeout: 5s
      retries: 10
      start_period: 30s

networks:
  backend-network:
    driver: bridge

volumes:
  pgdata:
    driver: local
EOF

    if [ ! -f "$DOCKER_DIR/docker-compose.yml" ]; then
        error "No se pudo crear el archivo docker-compose.yml"
        return 1
    fi
    
    chmod 644 "$DOCKER_DIR/docker-compose.yml"
    success "Archivo docker-compose.yml creado"
    return 0
}

# Función para ejecutar comandos en el directorio docker
run_in_docker_dir() {
    local cmd="$1"
    
    if [ ! -d "$DOCKER_DIR" ]; then
        error "Directorio $DOCKER_DIR no encontrado"
        return 1
    fi
    
    # Verificar permisos antes de continuar
    if ! check_and_fix_permissions "$DOCKER_DIR"; then
        error "No se puede acceder a $DOCKER_DIR por problemas de permisos"
        return 1
    fi
    
    cd "$DOCKER_DIR" || { error "No se puede acceder al directorio $DOCKER_DIR"; return 1; }
    eval "$cmd"
    local result=$?
    cd "$CURRENT_DIR" || return 1
    return $result
}

# Función MEJORADA para esperar a que PostgreSQL esté listo
wait_for_postgres() {
    process "Esperando a que PostgreSQL esté listo..."
    local max_attempts=30
    local attempt=1
    
    while [ $attempt -le $max_attempts ]; do
        if run_in_docker_dir "docker-compose exec -T db pg_isready -U postgres > /dev/null 2>&1"; then
            success "PostgreSQL está listo después de $attempt intentos"
            
            # Esperar un poco más para asegurar que esté completamente listo
            sleep 5
            
            # Verificar que la base de datos específica existe, si no crearla
            process "Verificando base de datos ${PROJECT_NAME}_db..."
            if ! run_in_docker_dir "docker-compose exec -T db psql -U postgres -d ${PROJECT_NAME}_db -c 'SELECT 1' > /dev/null 2>&1"; then
                process "Creando base de datos ${PROJECT_NAME}_db..."
                if run_in_docker_dir "docker-compose exec -T db createdb -U postgres ${PROJECT_NAME}_db"; then
                    success "Base de datos ${PROJECT_NAME}_db creada exitosamente"
                else
                    error "Error creando la base de datos ${PROJECT_NAME}_db"
                    return 1
                fi
            else
                success "Base de datos ${PROJECT_NAME}_db ya existe"
            fi
            
            sleep 2
            return 0
        fi
        
        if [ $attempt -eq 1 ]; then
            process "Esperando a que PostgreSQL se inicialice..."
        else
            process "Intento $attempt/$max_attempts - PostgreSQL aún no está listo..."
        fi
        
        sleep 3
        attempt=$((attempt + 1))
    done
    
    error "PostgreSQL no está respondiendo después de $max_attempts intentos"
    run_in_docker_dir 'docker-compose logs db'
    return 1
}

# Construir y levantar contenedores CON MEJOR MANEJO DE ERRORES
build_docker_containers() {
    process "Construyendo contenedores Docker..."
    
    if [ ! -d "$DOCKER_DIR" ]; then
        error "El directorio $DOCKER_DIR no existe. No se pueden construir los contenedores."
        return 1
    fi
    
    # Verificar permisos antes de continuar
    if ! check_and_fix_permissions "$DOCKER_DIR"; then
        error "No se pueden construir los contenedores por problemas de permisos"
        return 1
    fi
    
    # Parar contenedores existentes
    process "Deteniendo contenedores existentes..."
    if ! run_in_docker_dir 'docker-compose down 2>/dev/null'; then
        warning "No se pudieron detener contenedores existentes (puede ser normal si no hay contenedores)"
    fi
    
    # Construir imágenes con mejor manejo de errores
    process "Construyendo imágenes Docker..."
    
    # Primero intentar construir sin cache para detectar errores temprano
    if run_in_docker_dir "docker-compose build --no-cache --build-arg PHP_VERSION=${PHP_VERSION} 2>&1 | tee build.log"; then
        success "Imágenes construidas exitosamente sin cache"
    else
        error "Error construyendo las imágenes Docker sin cache"
        process "Analizando errores de construcción..."
        
        # Mostrar los últimos errores del log
        if [ -f "$DOCKER_DIR/build.log" ]; then
            error "Últimas líneas del log de construcción:"
            tail -20 "$DOCKER_DIR/build.log" | while read -r line; do
                echo "  $line"
            done
        fi
        
        # Intentar con cache como fallback
        warning "Intentando construcción con cache..."
        if ! run_in_docker_dir "docker-compose build --build-arg PHP_VERSION=${PHP_VERSION}"; then
            error "Error crítico en la construcción de imágenes Docker"
            return 1
        fi
    fi
    
    # Levantar contenedores
    process "Iniciando contenedores..."
    if ! run_in_docker_dir 'docker-compose up -d'; then
        error "Error iniciando contenedores"
        run_in_docker_dir 'docker-compose logs'
        return 1
    fi
    
    # Esperar a que PostgreSQL esté listo
    if ! wait_for_postgres; then
        error "La base de datos no se inicializó correctamente"
        return 1
    fi
    
    # Verificar estado de los contenedores
    process "Verificando estado de los contenedores..."
    if run_in_docker_dir 'docker-compose ps | grep -q "Up"'; then
        success "Contenedores Docker listos y ejecutándose"
        return 0
    else
        error "Los contenedores no se iniciaron correctamente"
        run_in_docker_dir 'docker-compose ps'
        return 1
    fi
}

# Ejecutar comandos en el contenedor web con reintentos MEJORADO
exec_in_web() {
    local cmd="$1"
    local max_attempts=3
    local attempt=1
    
    sleep 10
    
    while [ $attempt -le $max_attempts ]; do
        process "Ejecutando comando (intento $attempt/$max_attempts)..."
        
        if run_in_docker_dir "docker-compose exec -T web bash -c \"$cmd\""; then
            return 0
        fi
        
        if [ $attempt -lt $max_attempts ]; then
            warning "Intento $attempt falló. Reintentando en 10 segundos..."
            sleep 10
        fi
        
        attempt=$((attempt + 1))
    done
    
    error "Error ejecutando comando después de $max_attempts intentos: $cmd"
    return 1
}

# Función MEJORADA para crear proyectos con Composer
create_project_with_composer() {
    local framework_cmd="$1"
    local project_name="$2"
    local install_dir="$3"
    
    process "Descargando proyecto $framework_cmd..."
    
    exec_in_web "composer config -g process-timeout 1200"
    exec_in_web "composer config -g repo.packagist composer https://packagist.org"
    exec_in_web "composer clear-cache"
    
    # Opciones específicas por framework
    if [[ "$FRAMEWORK" == yii2* ]]; then
        local composer_options=(
            "--prefer-dist --no-progress --no-interaction"
            "--prefer-dist --no-dev --no-progress --no-interaction"
            "--prefer-source --no-progress --no-interaction"
        )
    else
        local composer_options=(
            "--prefer-dist --no-dev --no-progress --no-interaction"
            "--prefer-dist --no-progress --no-interaction"
            "--prefer-source --no-progress --no-interaction"
        )
    fi
    
    for options in "${composer_options[@]}"; do
        process "Intentando con opciones: $options"
        
        # Limpiar directorio temporal si existe
        exec_in_web "rm -rf /tmp/${project_name} 2>/dev/null || true"
        
        if exec_in_web "composer create-project $options $framework_cmd /tmp/${project_name}"; then
            # VERIFICAR que el proyecto se creó correctamente
            if exec_in_web "[ -d /tmp/${project_name} ] && [ -f /tmp/${project_name}/composer.json ]"; then
                success "Proyecto descargado correctamente"
                
                # Mover a la ubicación final
                exec_in_web "rm -rf ${install_dir}/* 2>/dev/null || true"
                exec_in_web "sh -c 'mv /tmp/${project_name}/* ${install_dir}/ 2>/dev/null || true'"
                exec_in_web "sh -c 'mv /tmp/${project_name}/.* ${install_dir}/ 2>/dev/null || true'"
                exec_in_web "rm -rf /tmp/${project_name}"
                
                # Instalar dependencias si es necesario
                if exec_in_web "[ ! -d ${install_dir}/vendor ] && [ -f ${install_dir}/composer.json ]"; then
                    process "Instalando dependencias..."
                    if exec_in_web "cd ${install_dir} && composer install --no-dev --no-progress --no-interaction"; then
                        success "Dependencias instaladas correctamente"
                    else
                        warning "No se pudieron instalar las dependencias automáticamente"
                    fi
                fi
                
                return 0
            fi
        fi
        
        warning "Intento falló con opciones: $options"
        exec_in_web "rm -rf /tmp/${project_name} 2>/dev/null || true"
    done
    
    return 1
}

# Crear proyecto Yii2 Basic
create_yii2_basic_project() {
    process "Creando proyecto Yii2 Basic: ${PROJECT_NAME}"
    
    if ! create_project_with_composer "yiisoft/yii2-app-basic" "${PROJECT_NAME}" "/var/www/html"; then
        error "Error al crear proyecto Yii2 Basic después de múltiples intentos"
        return 1
    fi
    
    # Configuraciones adicionales para Yii2 Basic
    exec_in_web "chown -R www-data:www-data /var/www/html"
    exec_in_web "chmod -R 755 /var/www/html/runtime"
    exec_in_web "chmod -R 755 /var/www/html/web/assets"
    
    success "Proyecto Yii2 Basic '${PROJECT_NAME}' creado correctamente"
    return 0
}

# Crear proyecto Yii2 Advanced
create_yii2_advanced_project() {
    process "Creando proyecto Yii2 Advanced: ${PROJECT_NAME}"
    
    if ! create_project_with_composer "yiisoft/yii2-app-advanced" "${PROJECT_NAME}" "/var/www/html"; then
        error "Error al crear proyecto Yii2 Advanced después de múltiples intentos"
        return 1
    fi
    
    # Configuraciones adicionales para Yii2 Advanced
    exec_in_web "chown -R www-data:www-data /var/www/html"
    exec_in_web "chmod -R 755 /var/www/html/frontend/runtime"
    exec_in_web "chmod -R 755 /var/www/html/backend/runtime"
    exec_in_web "chmod -R 755 /var/www/html/frontend/web/assets"
    exec_in_web "chmod -R 755 /var/www/html/backend/web/assets"
    
    # Inicializar el proyecto advanced
    process "Inicializando Yii2 Advanced..."
    if exec_in_web "cd /var/www/html && php init --env=Development --overwrite=All"; then
        success "Yii2 Advanced inicializado correctamente"
    else
        warning "No se pudo inicializar Yii2 Advanced automáticamente"
    fi
    
    success "Proyecto Yii2 Advanced '${PROJECT_NAME}' creado correctamente"
    return 0
}

# Crear proyecto Yii3
create_yii3_project() {
    process "Creando proyecto Yii3: ${PROJECT_NAME}"
    
    if ! create_project_with_composer "yiisoft/yii-project-template" "${PROJECT_NAME}" "/var/www/html"; then
        error "Error al crear proyecto Yii3 después de múltiples intentos"
        return 1
    fi
    
    exec_in_web "chown -R www-data:www-data /var/www/html"
    
    success "Proyecto Yii3 '${PROJECT_NAME}' creado correctamente"
    return 0
}

# Crear proyecto Laravel
create_laravel_project() {
    process "Creando proyecto Laravel: ${PROJECT_NAME}"
    
    if ! create_project_with_composer "laravel/laravel" "${PROJECT_NAME}" "/var/www/html"; then
        error "Error al crear proyecto Laravel después de múltiples intentos"
        return 1
    fi
    
    exec_in_web "chown -R www-data:www-data /var/www/html"
    exec_in_web "chmod -R 775 /var/www/html/storage"
    exec_in_web "chmod -R 775 /var/www/html/bootstrap/cache"
    
    # Configurar .env para Laravel
    process "Configurando Laravel..."
    exec_in_web "cd /var/www/html && cp .env.example .env 2>/dev/null || true"
    
    exec_in_web "cd /var/www/html && cat > .env << 'ENDFILE'
APP_NAME=${PROJECT_NAME}
APP_ENV=local
APP_KEY=
APP_DEBUG=true
APP_URL=http://localhost:${WEB_PORT}

DB_CONNECTION=pgsql
DB_HOST=db
DB_PORT=5432
DB_DATABASE=${PROJECT_NAME}_db
DB_USERNAME=postgres
DB_PASSWORD=password

LOG_CHANNEL=stack
LOG_DEPRECATIONS_CHANNEL=null
LOG_LEVEL=debug

QUEUE_CONNECTION=sync
SESSION_DRIVER=file
SESSION_LIFETIME=120

MEMCACHED_HOST=127.0.0.1

REDIS_HOST=127.0.0.1
REDIS_PASSWORD=null
REDIS_PORT=6379

MAIL_MAILER=smtp
MAIL_HOST=mailhog
MAIL_PORT=1025
MAIL_USERNAME=null
MAIL_PASSWORD=null
MAIL_ENCRYPTION=null
MAIL_FROM_ADDRESS=null
MAIL_FROM_NAME=\"\${APP_NAME}\"
ENDFILE"
    
    exec_in_web "cd /var/www/html && php artisan key:generate"
    
    success "Proyecto Laravel '${PROJECT_NAME}' creado correctamente"
    return 0
}

# Crear el proyecto según el framework seleccionado
create_framework_project() {
    process "Iniciando creación del proyecto ${PROJECT_NAME} con ${FRAMEWORK}..."
    
    process "Probando conexión a internet desde el contenedor..."
    if ! exec_in_web "curl -s --connect-timeout 30 https://packagist.org > /dev/null"; then
        warning "El contenedor no puede conectarse a Packagist. Esto puede causar problemas."
        exec_in_web "cat /etc/resolv.conf"
    else
        success "Conexión a internet verificada desde el contenedor"
    fi
    
    case "$FRAMEWORK" in
        "yii2-basic")
            create_yii2_basic_project
            ;;
        "yii2-advanced")
            create_yii2_advanced_project
            ;;
        "yii3")
            create_yii3_project
            ;;
        "laravel")
            create_laravel_project
            ;;
        *)
            error "Framework no válido: $FRAMEWORK"
            return 1
            ;;
    esac
}

# Configuración MEJORADA de base de datos
configure_database() {
    process "Configurando base de datos para $FRAMEWORK..."
    
    sleep 10
    
    case "$FRAMEWORK" in
        "yii2-basic")
            configure_yii2_basic_db
            ;;
        "yii2-advanced")
            configure_yii2_advanced_db
            ;;
        "yii3")
            configure_yii3_db
            ;;
        "laravel")
            configure_laravel_db
            ;;
    esac
}

configure_laravel_db() {
    process "Configurando base de datos para Laravel..."
    
    process "Verificando conexión a la base de datos..."
    if exec_in_web "cd /var/www/html && php -r \"
try {
    \\\$pdo = new PDO('pgsql:host=db;port=5432;dbname=${PROJECT_NAME}_db', 'postgres', 'password');
    \\\$pdo->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION);
    echo 'Conexión a BD exitosa' . PHP_EOL;
} catch (PDOException \\\$e) {
    echo 'Error de conexión: ' . \\\$e->getMessage() . PHP_EOL;
    exit(1);
}
\""; then
        success "Conexión a la base de datos verificada"
    else
        error "No se pudo conectar a la base de datos"
        return 1
    fi
    
    process "Ejecutando migraciones de Laravel..."
    if exec_in_web "cd /var/www/html && php artisan migrate --force"; then
        success "Migraciones de Laravel ejecutadas correctamente"
    else
        warning "Las migraciones de Laravel fallaron o no son necesarias"
    fi
    return 0
}

configure_yii3_db() {
    process "Configurando base de datos para Yii3..."
    if exec_in_web "cd /var/www/html && php ./yii migrate/up --interactive=0"; then
        success "Migraciones de Yii3 ejecutadas correctamente"
    else
        warning "Las migraciones de Yii3 fallaron o no son necesarias"
    fi
    return 0
}

# Configuración MEJORADA para Yii2 Basic
configure_yii2_basic_db() {
    process "Configurando base de datos para Yii2 Basic..."
    
    # Primero verificar que Yii2 esté instalado correctamente
    process "Verificando instalación de Yii2 Basic..."
    if ! exec_in_web "[ -f /var/www/html/vendor/yiisoft/yii2/Yii.php ]"; then
        error "Yii2 Basic no está instalado correctamente. No se puede configurar la base de datos."
        info "Instala las dependencias manualmente:"
        echo "  cd $DOCKER_DIR && docker-compose exec web composer install"
        return 1
    fi
    
    process "Configurando conexión a base de datos para Yii2 Basic..."
    exec_in_web "cd /var/www/html && cp config/db.php config/db.php.backup 2>/dev/null || true"
    
    exec_in_web "cd /var/www/html && cat > config/db.php << 'ENDFILE'
<?php

return [
    'class' => 'yii\db\Connection',
    'dsn' => 'pgsql:host=db;port=5432;dbname=${PROJECT_NAME}_db',
    'username' => 'postgres',
    'password' => 'password',
    'charset' => 'utf8',
];
ENDFILE"
    
    # Deshabilitar GII en producción o si no está instalado
    exec_in_web "cd /var/www/html && cp config/web.php config/web.php.backup 2>/dev/null || true"
    exec_in_web "cd /var/www/html && sed -i \"s/'modules' => \[/'modules' => \[\\n    /*\\n    'gii' => [\\n        'class' => 'yii\\\gii\\\Module',\\n    ],\\n    */\\n/g\" config/web.php 2>/dev/null || true"
    
    process "Intentando ejecutar migraciones de Yii2 Basic..."
    if exec_in_web "cd /var/www/html && php yii migrate/up --interactive=0"; then
        success "Migraciones de Yii2 Basic ejecutadas correctamente"
    else
        warning "Las migraciones de Yii2 Basic fallaron o no son necesarias"
    fi
    
    return 0
}

# Configuración para Yii2 Advanced
configure_yii2_advanced_db() {
    process "Configurando base de datos para Yii2 Advanced..."
    
    # Verificar instalación
    if ! exec_in_web "[ -f /var/www/html/vendor/yiisoft/yii2/Yii.php ]"; then
        error "Yii2 Advanced no está instalado correctamente."
        return 1
    fi
    
    # Configurar base de datos común
    exec_in_web "cd /var/www/html && cat > common/config/main-local.php << 'ENDFILE'
<?php
return [
    'components' => [
        'db' => [
            'class' => 'yii\db\Connection',
            'dsn' => 'pgsql:host=db;port=5432;dbname=${PROJECT_NAME}_db',
            'username' => 'postgres',
            'password' => 'password',
            'charset' => 'utf8',
        ],
    ],
];
ENDFILE"
    
    # Configurar base de datos para frontend y backend
    for app in frontend backend; do
        exec_in_web "cd /var/www/html && cat > ${app}/config/main-local.php << 'ENDFILE'
<?php
return [
    'components' => [
        'db' => [
            'class' => 'yii\db\Connection',
            'dsn' => 'pgsql:host=db;port=5432;dbname=${PROJECT_NAME}_db',
            'username' => 'postgres',
            'password' => 'password',
            'charset' => 'utf8',
        ],
    ],
];
ENDFILE"
    done
    
    process "Ejecutando migraciones de Yii2 Advanced..."
    if exec_in_web "cd /var/www/html && php yii migrate/up --interactive=0"; then
        success "Migraciones de Yii2 Advanced ejecutadas correctamente"
    else
        warning "Las migraciones de Yii2 Advanced fallaron o no son necesarias"
    fi
    
    return 0
}

# Mostrar resumen final MEJORADO
show_final_summary() {
    echo ""
    success "🎉 ¡PROYECTO CREADO EXITOSAMENTE!"
    echo "=================================="
    echo ""
    info "📊 RESUMEN FINAL:"
    echo "-----------------"
    echo "  📂 Proyecto: $PROJECT_NAME"
    echo "  🚀 Framework: $FRAMEWORK"
    echo "  🐘 PHP: $PHP_VERSION"
    echo "  🌐 URL: http://localhost:${WEB_PORT}"
    echo "  🗄️  Base de datos: localhost:${DB_PORT}"
    echo "  📁 Directorio: $DOCKER_DIR/src/$PROJECT_NAME/"
    echo ""
    
    # Información específica por framework
    case "$FRAMEWORK" in
        "yii2-advanced")
            info "🌐 URLs de Yii2 Advanced:"
            echo "  Frontend: http://localhost:${WEB_PORT}"
            echo "  Backend: http://localhost:${WEB_PORT}/backend/web"
            ;;
        *)
            info "🌐 URL de la aplicación:"
            echo "  http://localhost:${WEB_PORT}"
            ;;
    esac
    echo ""
    
    info "🚀 COMANDOS ÚTILES:"
    echo "------------------"
    echo "  Iniciar proyecto: cd $DOCKER_DIR && docker-compose up -d"
    echo "  Detener proyecto: cd $DOCKER_DIR && docker-compose down"
    echo "  Ver logs: cd $DOCKER_DIR && docker-compose logs"
    echo "  Acceder a la consola: cd $DOCKER_DIR && docker-compose exec web bash"
    echo ""
    
    info "🔗 CONEXIÓN A LA BASE DE DATOS:"
    echo "------------------------------"
    echo "  Host: localhost"
    echo "  Puerto: ${DB_PORT}"
    echo "  Base de datos: ${PROJECT_NAME}_db"
    echo "  Usuario: postgres"
    echo "  Contraseña: password"
    echo ""
    
    # Instrucciones específicas por framework
    case "$FRAMEWORK" in
        "yii2-basic"|"yii2-advanced")
            info "🔧 INSTRUCCIONES ESPECÍFICAS PARA YII2:"
            echo "-------------------------------------"
            echo "  Si hay problemas con dependencias:"
            echo "    cd $DOCKER_DIR && docker-compose exec web composer install"
            if [ "$FRAMEWORK" = "yii2-advanced" ]; then
                echo "  Para inicializar el proyecto:"
                echo "    cd $DOCKER_DIR && docker-compose exec web php init"
            fi
            echo "  Para ejecutar migraciones:"
            echo "    cd $DOCKER_DIR && docker-compose exec web php yii migrate/up --interactive=0"
            echo "  Para instalar módulos de desarrollo:"
            echo "    cd $DOCKER_DIR && docker-compose exec web composer require --dev yiisoft/yii2-gii"
            echo "    cd $DOCKER_DIR && docker-compose exec web composer require --dev yiisoft/yii2-debug"
            echo ""
            ;;
        "yii3")
            info "🔧 INSTRUCCIONES ESPECÍFICAS PARA YII3:"
            echo "-------------------------------------"
            echo "  Para ejecutar migraciones: cd $DOCKER_DIR && docker-compose exec web php ./yii migrate/up --interactive=0"
            echo ""
            ;;
        "laravel")
            info "🔧 INSTRUCCIONES ESPECÍFICAS PARA LARAVEL:"
            echo "----------------------------------------"
            echo "  Para ejecutar migraciones: cd $DOCKER_DIR && docker-compose exec web php artisan migrate"
            echo "  Para generar key: cd $DOCKER_DIR && docker-compose exec web php artisan key:generate"
            echo ""
            ;;
    esac
    
    info "📝 PRÓXIMOS PASOS:"
    echo "-----------------"
    echo "  1. Accede a la URL mostrada arriba para ver tu aplicación"
    echo "  2. Configura la base de datos si es necesario"
    echo "  3. ¡Comienza a desarrollar!"
    echo ""
}

# Función principal
main() {
    clear
    echo "========================================"
    echo "  GENERADOR DE PROYECTOS CON DOCKER v1.1"
    echo "========================================"
    echo ""
    
    check_docker_installation
    check_docker_permissions
    check_internet_connection
    
    select_framework
    
    get_project_name
    
    if ! create_directory_structure; then
        error "Error creando la estructura de directorios"
        exit 1
    fi
    
    if ! create_env_file; then
        error "Error creando el archivo .env"
        exit 1
    fi
    
    if ! create_apache_config; then
        error "Error creando la configuración de Apache"
        exit 1
    fi
    
    if ! create_dockerfile; then
        error "Error creando el Dockerfile"
        exit 1
    fi
    
    if ! create_docker_compose; then
        error "Error creando docker-compose.yml"
        exit 1
    fi
    
    if build_docker_containers; then
        process "Esperando inicialización completa de servicios..."
        sleep 20
        
        if create_framework_project; then
            process "Configurando la aplicación..."
            if configure_database; then
                show_final_summary
            else
                warning "Proyecto creado pero la configuración de BD tuvo problemas menores"
                show_final_summary
            fi
        else
            error "Error al crear el proyecto del framework"
            exit 1
        fi
    else
        error "Error al construir los contenedores Docker"
        exit 1
    fi
}

if [[ "${BASH_SOURCE[0]}" == "${0}" ]]; then
    main "$@"
fi
-- RISK326	integrity_check
-- contains(user_agent, "integrity=false") or contains(user_agent, "integrity=False"
DROP TABLE team_kingkong.tpap_risk326_breaches;

-- CREATE TABLE team_kingkong.tpap_risk326_breaches AS
INSERT INTO team_kingkong.tpap_risk326_breaches
SELECT DISTINCT B.*, C.category
, IF(D.upi_subtype IS NOT NULL, D.upi_subtype, IF(C.category = 'LITE_MANDATE', 'UPI_LITE_MANDATE', '')) AS upi_subtype
, D.integrity
, 'integrity_check'  AS rule_name
, 'user agent intergrity = false' AS breach_reason FROM
    (SELECT txn_id,
    MAX(CASE WHEN participant_type = 'PAYER' THEN vpa END) AS payer_vpa,
    MAX(CASE WHEN participant_type = 'PAYEE' THEN vpa END) AS payee_vpa,
    MAX(DATE(created_on)) as txn_date,
    MAX(amount) AS txn_amount,
    MAX(created_on) AS txn_time
    FROM switch.txn_participants_snapshot_v3
    WHERE DATE(dl_last_updated) BETWEEN DATE'2025-01-01' AND DATE'2025-01-31'
    AND DATE(created_on) BETWEEN DATE'2025-01-01' AND DATE'2025-01-31' 
    GROUP BY 1)B
inner join
    (select txn_id, category
    from switch.txn_info_snapshot_v3
    where DATE(dl_last_updated) BETWEEN DATE'2025-01-01' AND DATE'2025-01-31'
    and DATE(created_on) BETWEEN DATE'2025-01-01' AND DATE'2025-01-31')C
on B.txn_id = C.txn_id
INNER JOIN
    (SELECT txnid
    , regexp_replace(cast(json_extract(request, '$.evaluationType') as varchar), '"', '') AS upi_subtype
    , json_extract_scalar(request, '$.requestPayload.txnType') AS txnType
    , json_extract_scalar(request, '$.requestPayload.integrity') AS integrity -- true UNKNOWN false
    FROM tpap_hss.upi_switchv2_dwh_risk_data_snapshot_v3
    WHERE DATE(dl_last_updated) BETWEEN DATE'2025-01-01' AND DATE'2025-01-31'
    AND json_extract_scalar(response, '$.action_recommended') <> 'BLOCK'
    AND regexp_replace(cast(json_extract(request, '$.evaluationType') as varchar), '"', '') = 'UPI_TRANSACTION'
    AND json_extract_scalar(request, '$.requestPayload.integrity') <> 'true')D
ON B.txn_id = D.txnid
;

SELECT MONTH(txn_date), COUNT(*) FROM team_kingkong.tpap_risk326_breaches GROUP BY 1 LIMIT 10;
star

Mon Sep 29 2025 04:57:52 GMT+0000 (Coordinated Universal Time) https://uphex.com/features/ai/

@melvinoleson

star

Sun Sep 28 2025 10:43:38 GMT+0000 (Coordinated Universal Time) https://www.tekrevol.com/mobile-app-development

@charlesberline #business #webdevelopment #mobileappdevelopment

star

Sat Sep 27 2025 12:48:13 GMT+0000 (Coordinated Universal Time)

@vanthien

star

Fri Sep 26 2025 17:04:25 GMT+0000 (Coordinated Universal Time)

@jrg_300i ##yii2

star

Fri Sep 26 2025 14:53:35 GMT+0000 (Coordinated Universal Time)

@Inescn

star

Fri Sep 26 2025 14:03:49 GMT+0000 (Coordinated Universal Time)

@Inescn

star

Fri Sep 26 2025 12:57:40 GMT+0000 (Coordinated Universal Time)

@jrg_300i ##yii2

star

Thu Sep 25 2025 08:28:58 GMT+0000 (Coordinated Universal Time)

@Inescn

star

Wed Sep 24 2025 09:59:00 GMT+0000 (Coordinated Universal Time) https://dnevnik.ru/r/irkutsk/marks

@soid

star

Wed Sep 24 2025 09:49:14 GMT+0000 (Coordinated Universal Time)

@Inescn

star

Wed Sep 24 2025 09:26:59 GMT+0000 (Coordinated Universal Time)

@Pulak

star

Wed Sep 24 2025 09:25:48 GMT+0000 (Coordinated Universal Time)

@Pulak

star

Tue Sep 23 2025 13:07:53 GMT+0000 (Coordinated Universal Time)

@Saravana_Kumar #postgres

star

Tue Sep 23 2025 13:06:42 GMT+0000 (Coordinated Universal Time)

@Saravana_Kumar #bash

star

Tue Sep 23 2025 12:32:27 GMT+0000 (Coordinated Universal Time)

@mehran

star

Tue Sep 23 2025 12:31:39 GMT+0000 (Coordinated Universal Time)

@mehran

star

Tue Sep 23 2025 12:28:50 GMT+0000 (Coordinated Universal Time)

@jrg_300i

star

Tue Sep 23 2025 11:00:24 GMT+0000 (Coordinated Universal Time) https://www.thecryptoape.com/erc20-token-development

@Davidbrevis #ethereumtoken development #erc20token development #erc20token development company

star

Tue Sep 23 2025 10:56:57 GMT+0000 (Coordinated Universal Time)

@Inescn

star

Tue Sep 23 2025 10:44:16 GMT+0000 (Coordinated Universal Time) https://maticz.com/tap-to-earn-game-development

@carolinemax

star

Tue Sep 23 2025 06:24:37 GMT+0000 (Coordinated Universal Time) https://www.addustechnologies.com/crypto-forex-trading-platform-development

@brucebanner #crypto #forex #trading #python

star

Mon Sep 22 2025 20:14:52 GMT+0000 (Coordinated Universal Time)

@jrg_300i

star

Mon Sep 22 2025 19:56:20 GMT+0000 (Coordinated Universal Time)

@jrg_300i

star

Mon Sep 22 2025 18:21:57 GMT+0000 (Coordinated Universal Time)

@mehran

star

Mon Sep 22 2025 16:17:34 GMT+0000 (Coordinated Universal Time)

@1234_5

star

Mon Sep 22 2025 12:04:42 GMT+0000 (Coordinated Universal Time)

@MinaTimo

star

Mon Sep 22 2025 10:27:59 GMT+0000 (Coordinated Universal Time) https://innosoft-group.com/sportsbook-software-providers/

@Margaret1601 #sportsbooksoftware providers in dubai #sportsbooksoftware providers #sportsbooksoftware provider

star

Mon Sep 22 2025 09:59:50 GMT+0000 (Coordinated Universal Time)

@Inescn

star

Mon Sep 22 2025 09:15:55 GMT+0000 (Coordinated Universal Time) https://www.kryptobees.com/cryptocurrency-exchange-development-company

@Franklinclas

star

Mon Sep 22 2025 07:51:26 GMT+0000 (Coordinated Universal Time) https://www.sevenmentor.com/machine-learning-course-in-pune.php

@priya023

star

Mon Sep 22 2025 07:34:22 GMT+0000 (Coordinated Universal Time) https://www.makeuseof.com/windows-features-dont-exist-without-powershell/?utm_source

@darkoeller

star

Sun Sep 21 2025 20:49:17 GMT+0000 (Coordinated Universal Time)

@oforey #python

star

Sun Sep 21 2025 20:37:04 GMT+0000 (Coordinated Universal Time)

@oforey #python

star

Sun Sep 21 2025 20:32:36 GMT+0000 (Coordinated Universal Time)

@oforey #python

star

Sun Sep 21 2025 20:23:08 GMT+0000 (Coordinated Universal Time)

@oforey #python

star

Sun Sep 21 2025 20:08:57 GMT+0000 (Coordinated Universal Time)

@oforey

star

Sat Sep 20 2025 23:24:54 GMT+0000 (Coordinated Universal Time)

@FOHWellington

star

Sat Sep 20 2025 16:30:36 GMT+0000 (Coordinated Universal Time) https://economyhomedecor.com/

@allmaxh

star

Fri Sep 19 2025 16:50:47 GMT+0000 (Coordinated Universal Time)

@freepythoncode ##python #coding #python

star

Fri Sep 19 2025 15:44:32 GMT+0000 (Coordinated Universal Time) https://huggingface.co/Alibaba-NLP/Tongyi-DeepResearch-30B-A3B

@GADJI123

star

Fri Sep 19 2025 12:06:55 GMT+0000 (Coordinated Universal Time)

@usman13

star

Fri Sep 19 2025 10:37:23 GMT+0000 (Coordinated Universal Time) https://maticz.com/zk-tech-development

@carolinemax

star

Fri Sep 19 2025 07:17:23 GMT+0000 (Coordinated Universal Time)

@usman13

star

Thu Sep 18 2025 16:01:46 GMT+0000 (Coordinated Universal Time)

@jrg_300i #docker

star

Thu Sep 18 2025 07:21:38 GMT+0000 (Coordinated Universal Time)

@shubhangi.b

star

Thu Sep 18 2025 06:35:14 GMT+0000 (Coordinated Universal Time) https://www.addustechnologies.com/crypto-trading-bot-development-company

@brucebanner #crypto #trading #bot

Save snippets that work with our extensions

Available in the Chrome Web Store Get Firefox Add-on Get VS Code extension