Index: common/db/job.php
===================================================================
--- common/db/job.php	(revision 1167)
+++ common/db/job.php	(working copy)
@@ -23,16 +23,27 @@ require_once(IA_ROOT_DIR."common/db/db.php");
 // Creates new eval job
 function job_create($task_id, $round_id, $user_id, $compiler_id, $file_contents,
         $remote_ip_info = null) {
+    /**
+     * Check which submission is the current one(first, second, ...)
+     * Counting from 0
+     */
+    $submission = task_user_get_submits($user_id, $round_id, $task_id);
     $query = <<<SQL
         INSERT INTO ia_job
             (`task_id`, `round_id`, `user_id`, `compiler_id`, `file_contents`,
-             `submit_time`, `remote_ip_info`)
-        VALUES (%s, %s, %s, %s, %s, %s, %s)
+             `submit_time`, `remote_ip_info`, `submissions`)
+        VALUES (%s, %s, %s, %s, %s, %s, %s, %s)
 SQL;
     $query = sprintf($query,
             db_quote($task_id), db_quote($round_id), db_quote($user_id),
             db_quote($compiler_id), db_quote($file_contents),
-            db_quote(db_date_format()), db_quote($remote_ip_info));
+            db_quote(db_date_format()), db_quote($remote_ip_info),
+            db_quote($submission));
+
+    /**
+     * Increment the submission count
+     */
+    task_user_submit($user_id, $round_id, $task_id);
     return db_query($query);
 }
 
@@ -43,7 +54,7 @@ function job_get_next_job() {
 SELECT `job`.`id`, `job`.`user_id`, `job`.`task_id`, `job`.`round_id`,
        `job`.`compiler_id`, `job`.`status`, `job`.`submit_time`,
        `job`.`eval_message`, `job`.`score`, `job`.`file_contents`,
-       `job`.`remote_ip_info`
+       `job`.`remote_ip_info`, `job`.`submissions`
     FROM `ia_job` AS `job`
     WHERE `job`.`id` = (
         SELECT MIN(id)
@@ -87,7 +98,7 @@ function job_get_by_id($job_id, $contents = false) {
     log_assert(is_whole_number($job_id));
     $field_list = "`job`.`id`, job.`user_id`, `job`.`compiler_id`, `job`.`status`,
                    `job`.`submit_time`, `job`.`eval_message`, `job`.`score`,
-                   `job`.`eval_log`, `job`.`remote_ip_info`,
+                   `job`.`eval_log`, `job`.`remote_ip_info`, `job`.`submissions`,
                    OCTET_LENGTH(`job`.`file_contents`) AS `job_size`,
                    `user`.`username` AS `user_name`, `user`.`full_name` AS `user_fullname`,
                    `task`.`id` AS `task_id`,
@@ -100,7 +111,8 @@ function job_get_by_id($job_id, $contents = false) {
                    `round`.`title` AS `round_title`,
                    `round`.`type` AS `round_type`,
                    `round`.`state` AS `round_state`,
-                   `round`.`public_eval` AS `round_public_eval`";
+                   `round`.`public_eval` AS `round_public_eval`,
+                   `round`.`start_time` AS `round_start_time`";
     if ($contents) {
         $field_list .= ", job.file_contents";
     }
Index: common/db/task.php
===================================================================
--- common/db/task.php	(revision 1167)
+++ common/db/task.php	(working copy)
@@ -369,4 +369,43 @@ function task_get_user_score($task_id, $user_id, $round_id = 'arhiva') {
     return getattr($res, "score", null);
 }
 
+/**
+ * Returns the number of previous submits of an users to a task in a contest
+ *
+ * @param int $user_id
+ * @param string $round_id
+ * @param string $task_id
+ * @return int
+ */
+function task_user_get_submits($user_id, $round_id, $task_id) {
+    log_assert(is_user_id($user_id));
+    log_assert(is_round_id($round_id));
+    log_assert(is_task_id($task_id));
+
+    $query = sprintf("SELECT `submits` FROM ia_score_user_round_task
+            WHERE `user_id` = %s AND `round_id` = %s AND `task_id` = %s",
+            db_quote($user_id), db_quote($round_id), db_quote($task_id));
+    $res = db_fetch($query);
+
+    return getattr($res, "submits", 0);
+}
+
+/**
+ * Increment the submission counter for a specific user, round and task
+ *
+ * @param int $user_id
+ * @param string $round_id
+ * @param string $task_id
+ * @parm int $submission
+ */
+function task_user_submit($user_id, $round_id, $task_id) {
+    log_assert(is_user_id($user_id));
+    log_assert(is_round_id($round_id));
+    log_assert(is_task_id($task_id));
+
+    $query = sprintf('INSERT INTO `ia_score_user_round_task` VALUES (%s,%s,%s'
+            . ', 0, 1) ON DUPLICATE KEY UPDATE `submits` = `submits` + 1',
+           db_quote($user_id), db_quote($round_id), db_quote($task_id));
+    db_query($query);
+}
 ?>
Index: common/round.php
===================================================================
--- common/round.php	(revision 1167)
+++ common/round.php	(working copy)
@@ -5,6 +5,7 @@
 function round_get_types() {
     return array(
             'classic' => 'Concurs clasic',
+            'penalty-round' => 'Concurs cu penalizare',
             'archive' => 'Arhiva de pregatire',
             'user-defined' => 'Concurs virtual',
     );
@@ -28,6 +29,45 @@ function round_get_parameter_infos() {
                             'type' => 'bool',
                     ),
                 ),
+            'penalty-round' => array(
+                    'duration' => array(
+                            'name' => 'Durata',
+                            'description' => 'Durata concursului, in ore',
+                            'default' => '2',
+                            'type' => 'float',
+                    ),
+                    'rating_update' => array(
+                            'name' => 'Afecteaza rating-urile',
+                            'description' => 'Daca rezultatele din acest concu'
+                                . 'rs afecteaza rating-urile concurentilor',
+                            'default' => '1',
+                            'type' => 'bool',
+                    ),
+                    'decay_period' => array(
+                            'name' => 'Perioada de pierdere a unui procent din'
+                                . ' punctaj',
+                            'description' => 'Intervalul de timp(in secunde) '
+                                . 'in care un concurent pierde 1 procent din '
+                                . 'punctajul sau',
+                            'default' => '120',
+                            'type' => 'integer',
+                    ),
+                    'submit_cost' => array(
+                            'name' => 'Procentajul pierdut pe submisie',
+                            'description' => 'Procentul din punctaj pierdut pe'
+                                . ' submisii ulterioare',
+                            'default' => '3',
+                            'type' => 'integer',
+                    ),
+                    'minimum_score' => array(
+                            'name' => 'Procentul minim pe problema',
+                            'description' => 'Procentul minim din punctaj pe c'
+                                . 'are il poate obtine un concurent pe o probl'
+                                . 'ema rezolvata corect',
+                            'default' => '50',
+                            'type' => 'integer',
+                    ),
+                ),
             'archive' => array(
                     'duration' => array(
                             'name' => 'Durata',
@@ -50,8 +90,9 @@ function round_get_parameter_infos() {
 // Validate parameters. Return erros as $form_errors convention.
 function round_validate_parameters($round_type, $parameters) {
     $errors = array();
-    if ($round_type == 'classic' or $round_type == 'user-defined'
-            or $round_type == 'archive') {
+    if ($round_type == 'classic' || $round_type == 'user-defined'
+            || $round_type == 'archive'
+            || $round_type == 'penalty-round') {
         // Check duration
         $duration = getattr($parameters, 'duration');
         if (is_null($duration)) {
Index: eval/eval
===================================================================
--- eval/eval	(revision 1167)
+++ eval/eval	(working copy)
@@ -51,6 +51,28 @@ function job_handle($job) {
         log_error("Nu stiu sa evaluez task-uri de tip ".$task['type']);
     }
 
+    $submit_count = 0;
+    /**
+     * Check if the task was in a penalty type round
+     * Also increase the submit_count
+     */
+    $round = round_get($job['round_id']);
+    $round_parameters = round_get_parameters($job['round_id']);
+    if ($round['type'] == 'penalty-round') {
+        /*
+         * Check the number of submits (so we can add to penalty)
+         */
+         if ($job_result['score'] > 0) {
+             $percent = max($round_parameters['minimum_score'], 100 -
+                    $job['submissions'] * $round_parameters['submit_cost'] -
+                    (int)((db_date_parse($job['submit_time']) -
+                        db_date_parse($round['start_time'])) /
+                    $round_parameters['decay_period']));
+             $job_result['score'] = (int)($percent * $job_result['score']
+                    / 100);
+         }
+    }
+
     // Send job result and update score magic.
     job_send_result($job['id'], $job_result);
     score_update_for_job(
Index: scripts/migrate-ia-monthly
new file mode 100755
===================================================================
--- scripts/migrate-ia-monthly	(revision 0)
+++ scripts/migrate-ia-monthly	(working copy)
@@ -0,0 +1,26 @@
+#! /usr/bin/env php
+<?php
+require_once(dirname($argv[0]) . '/utilities.php');
+require_once(IA_ROOT_DIR . 'common/db/db.php');
+require_once(IA_ROOT_DIR . 'common/db/db_mysql.php');
+db_connect();
+
+$result = db_fetch('DESCRIBE `ia_round` `type`');
+if (getattr($result, 'Type') == "enum('classic','archive','user-defined')") {
+    db_query("ALTER TABLE `ia_round` modify `type` enum('classic','archive',"
+            . "'user-defined','penalty-round') DEFAULT NULL");
+}
+
+$result = db_fetch('DESCRIBE `ia_job` `submissions`');
+if (getattr($result, 'Type') == null) {
+    db_query('ALTER TABLE `ia_job` add column `submissions` int'
+            . '(11) DEFAULT 0');
+}
+
+$result = db_fetch('DESCRIBE `ia_score_user_round_task` `submits`');
+if (getattr($result, 'Type') == null) {
+    db_query('ALTER TABLE `ia_score_user_round_task` add column `submits` int'
+            . '(11) DEFAULT 1');
+}
+
+?>
Index: www/controllers/job_detail.php
===================================================================
--- www/controllers/job_detail.php	(revision 1167)
+++ www/controllers/job_detail.php	(working copy)
@@ -63,6 +63,46 @@ function controller_job_view($job_id) {
                 $view['group_score'][$group] = 0;
             }
         }
+
+        /**
+         * Get penalty amount and description
+         */
+        if ($job['round_type'] == 'penalty-round') {
+            $round_parameters = round_get_parameters($job['round_id']);
+            $description = '';
+            $amount = 0;
+
+            /**
+             * Don't add time penalty if it is 0
+             */
+            $time_penalty = (int)((db_date_parse($job['submit_time']) -
+                    db_date_parse($job['round_start_time'])) /
+                    $round_parameters['decay_period']);
+
+            if ($time_penalty > 0) {
+                $description .= sprintf("%d(pentru %.1f minute)",
+                        $time_penalty,
+                        ((db_date_parse($job['submit_time'])) -
+                            db_date_parse($job['round_start_time'])) / 60);
+                $amount += $time_penalty;
+            }
+            /**
+             * Don't add submit penalty if it is 0
+             */
+            if ($job['submissions'] > 0) {
+                if ($description != '') {
+                    $description .= ' + ';
+                }
+                $description .= sprintf("%d(pentru %s)",
+                        $job['submissions'] * $round_parameters['submit_cost'],
+                        $job['submissions'] == 1 ? 'o submisie' :
+                        ($job['submissions'] . ' submisii'));
+                $amount += $job['submissions'] * $round_parameters['submit_cost'];
+            }
+
+            $view['job']['penalty']['amount'] = $amount;
+            $view['job']['penalty']['description'] = $description;
+        }
     } else {
         $view['group_tests'] = false;
         $view['job']['score'] = NULL;
Index: www/macros/macro_tasks.php
===================================================================
--- www/macros/macro_tasks.php	(revision 1167)
+++ www/macros/macro_tasks.php	(working copy)
@@ -73,6 +73,29 @@ function task_row_style($row) {
     }
 }
 
+/**
+ * Returns whether or not an user received a score different than 0
+ * For use in penalty-round tasks
+ * @param array $row   the task in the round for which the above question
+ *                     is asked
+ * @return string
+ */
+function task_row_style_absolute($row) {
+    $score = getattr($row, 'score');
+    if (is_null($score)) {
+        return '';
+    }
+
+    log_assert(is_numeric($score));
+    $score = (int)$score;
+
+    if ($score > 0) {
+        return 'solved';
+    } else {
+        return 'tried';
+    }
+}
+
 function task_list_tabs($round_page, $active) {
     $tabs = array();
 
@@ -163,7 +186,12 @@ function macro_tasks($args) {
 
     $options['total_entries'] = round_get_task_count(
              $round_id, $user_id, $filter);
-    $options['row_style'] = 'task_row_style';
+
+    if (getattr($args, 'absolute_style') !== null) {
+        $options['row_style'] = 'task_row_style_absolute';
+    } else {
+        $options['row_style'] = 'task_row_style';
+    }
     $options['css_row_parity'] = true;
 
     $options['css_class'] = 'tasks sortable';
Index: www/views/job_detail.php
===================================================================
--- www/views/job_detail.php	(revision 1167)
+++ www/views/job_detail.php	(working copy)
@@ -107,6 +107,12 @@ $show_groups = $view['group_tests'] &&
         }
         echo '</tr>';
     }
+    if (getattr($job, 'penalty') !== null) {
+        echo '<tr><td colspan="'.($show_groups ? 5 : 4).'"> Penalizare '
+                . $job['penalty']['description'] . '</td><td class="number">'
+                . $job['penalty']['amount'] . '</td></tr>';
+    }
+
     if (!is_null($job['score'])) {
         echo '<tr><td colspan="'.($show_groups ? 5 : 4).'">'.
              'Punctaj total</td><td class="total_score number">'.$job['score'].'</td></tr>';
Index: www/views/round_create.php
===================================================================
--- www/views/round_create.php	(revision 1167)
+++ www/views/round_create.php	(working copy)
@@ -13,7 +13,7 @@ $form_fields = array(
                 'type' => 'enum',
                 'values' => $round_types,
                 'default' => 'user-defined',
-        ), 
+        ),
 );
 
 ?>
