<commit_before># Copyright 2011 the Melange authors.
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
#   http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.

"""Module for the GSoC slot transfer admin page."""

import json
import logging

from google.appengine.ext import db

from django import http

from melange.request import access
from melange.request import exception
from soc.views import template
from soc.views.helper import lists
from soc.views.helper import url_patterns

from soc.modules.gsoc.models.slot_transfer import GSoCSlotTransfer
from soc.modules.gsoc.views import base
from soc.modules.gsoc.views.helper import url_patterns as gsoc_url_patterns


class SlotsTransferAdminList(template.Template):
  """Template for list of slot transfer requests."""

  def __init__(self, data):
    self.data = data

    list_config = lists.ListConfiguration()
    # hidden key
    list_config.addPlainTextColumn(
        'full_transfer_key', 'Full slot transfer key',
        lambda ent, *args: str(ent.key()), hidden=True)
    list_config.addPlainTextColumn(
        'org', 'Organization',
        lambda e, *args: e.parent().short_name.strip(), width=75)
    options = [('', 'All'), ('pending', 'Pending'),
               ('accepted', 'Accepted'), ('rejected', 'Rejected')]
    list_config.addSimpleColumn('status', 'Status', width=40, options=options)
    list_config.addSimpleColumn('remarks', 'Remarks', width=75)
    list_config.addSimpleColumn('nr_slots', 'Returned slots', width=50)
    list_config.setColumnEditable('nr_slots', True)
    list_config.addSimpleColumn('admin_remarks', 'Admin remarks')
    list_config.setColumnEditable('admin_remarks', True) #, edittype='textarea')
    list_config.addNumericalColumn(
        'slots_desired', 'Min desired',
        lambda e, *args: e.parent().slots_desired, width=25, hidden=True)
    list_config.addNumericalColumn(
        'max_slots_desired', 'Max desired',
        lambda e, *args: e.parent().max_slots_desired, width=25, hidden=True)
    list_config.addNumericalColumn(
        'slots', 'Slots',
        lambda e, *args: e.parent().slots, width=50, hidden=True)
    list_config.setDefaultPagination(False)
    list_config.setDefaultSort('org')
    list_config.addPostEditButton('save', "Save", "",
                                  ['full_transfer_key'], refresh="none")

    bounds = [1,'all']
    keys = ['key', 'full_transfer_key']
    list_config.addPostButton('accept', "Accept", "", bounds, keys)
    list_config.addPostButton('reject', "Reject", "", bounds, keys)

    self._list_config = list_config

  def context(self):
    description = 'List of slot transfer requests for the program %s' % (
        self.data.program.name)

    slot_transfer_request_list = lists.ListConfigurationResponse(
        self.data, self._list_config, 0, description)

    return {
        'lists': [slot_transfer_request_list],
    }

  def post(self):
    idx = lists.getListIndex(self.data.request)
    if idx != 0:
      return False

    data = self.data.POST.get('data')

    button_id = self.data.POST.get('button_id')

    if not data:
      raise exception.BadRequest(message="Missing data")

    parsed = json.loads(data)

    if button_id == 'accept':
      return self.postAccept(parsed, True)

    if button_id == 'reject':
      return self.postAccept(parsed, False)

    if button_id == 'save':
      return self.postSave(parsed)

  def postAccept(self, data, accept):
    for properties in data:
      if 'full_transfer_key' not in properties:
        logging.warning("Missing key in '%s'", properties)
        continue

      slot_transfer_key = properties['full_transfer_key']
      def accept_slot_transfer_txn():
        slot_transfer = db.get(slot_transfer_key)

        if not slot_transfer:
          logging.warning("Invalid slot_transfer_key '%s'", slot_transfer_key)
          return

        org = slot_transfer.parent()
        if not org:
          logging.warning("No organization present for the slot transfer %s",
                          slot_transfer_key)
          return

        if accept:
          if slot_transfer.status == 'accepted':
            return

          slot_transfer.status = 'accepted'

          if slot_transfer.nr_slots < 0:
            logging.warning(
                "Organization %s is trying to trick us to gain more slots by "
                "using a negative number %s", org.name, slot_transfer.nr_slots)
            return

          org.slots -= slot_transfer.nr_slots
          if org.slots < 0:
            org.slots = 0

          org.put()
        else:
          if slot_transfer.status == 'rejected':
            return

          if slot_transfer.status == 'accepted':
            org.slots += slot_transfer.nr_slots
            org.put()

          slot_transfer.status = 'rejected'
        slot_transfer.put()

      db.run_in_transaction(accept_slot_transfer_txn)

    return True

  def postSave(self, parsed):

    for key_name, properties in parsed.iteritems():
      admin_remarks = properties.get('admin_remarks')
      nr_slots = properties.get('nr_slots')
      full_transfer_key = properties.get('full_transfer_key')

      if not full_transfer_key:
        logging.warning(
            "key for the slot transfer request is not present '%s'",
            properties)
        continue

      if 'admin_remarks' not in properties and 'nr_slots' not in properties:
        logging.warning(
            "Neither admin remarks or number of slots present in '%s'",
            properties)
        continue

      if 'nr_slots' in properties:
        if not nr_slots.isdigit():<commit_msg>Post handler in SlotsTransferAdminList is updated to NDB regarding organizations.<commit_after># Copyright 2011 the Melange authors.
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
#   http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.

"""Module for the GSoC slot transfer admin page."""

import json
import logging

from google.appengine.ext import db
from google.appengine.ext import ndb

from django import http

from melange.request import access
from melange.request import exception
from soc.views import template
from soc.views.helper import lists
from soc.views.helper import url_patterns

from soc.modules.gsoc.models.slot_transfer import GSoCSlotTransfer
from soc.modules.gsoc.views import base
from soc.modules.gsoc.views.helper import url_patterns as gsoc_url_patterns


class SlotsTransferAdminList(template.Template):
  """Template for list of slot transfer requests."""

  def __init__(self, data):
    self.data = data

    list_config = lists.ListConfiguration()
    # hidden key
    list_config.addPlainTextColumn(
        'full_transfer_key', 'Full slot transfer key',
        lambda ent, *args: str(ent.key()), hidden=True)
    list_config.addPlainTextColumn(
        'org', 'Organization',
        lambda e, *args: e.parent().short_name.strip(), width=75)
    options = [('', 'All'), ('pending', 'Pending'),
               ('accepted', 'Accepted'), ('rejected', 'Rejected')]
    list_config.addSimpleColumn('status', 'Status', width=40, options=options)
    list_config.addSimpleColumn('remarks', 'Remarks', width=75)
    list_config.addSimpleColumn('nr_slots', 'Returned slots', width=50)
    list_config.setColumnEditable('nr_slots', True)
    list_config.addSimpleColumn('admin_remarks', 'Admin remarks')
    list_config.setColumnEditable('admin_remarks', True) #, edittype='textarea')
    list_config.addNumericalColumn(
        'slots_desired', 'Min desired',
        lambda e, *args: e.parent().slots_desired, width=25, hidden=True)
    list_config.addNumericalColumn(
        'max_slots_desired', 'Max desired',
        lambda e, *args: e.parent().max_slots_desired, width=25, hidden=True)
    list_config.addNumericalColumn(
        'slots', 'Slots',
        lambda e, *args: e.parent().slots, width=50, hidden=True)
    list_config.setDefaultPagination(False)
    list_config.setDefaultSort('org')
    list_config.addPostEditButton('save', "Save", "",
                                  ['full_transfer_key'], refresh="none")

    bounds = [1,'all']
    keys = ['key', 'full_transfer_key']
    list_config.addPostButton('accept', "Accept", "", bounds, keys)
    list_config.addPostButton('reject', "Reject", "", bounds, keys)

    self._list_config = list_config

  def context(self):
    description = 'List of slot transfer requests for the program %s' % (
        self.data.program.name)

    slot_transfer_request_list = lists.ListConfigurationResponse(
        self.data, self._list_config, 0, description)

    return {
        'lists': [slot_transfer_request_list],
    }

  def post(self):
    idx = lists.getListIndex(self.data.request)
    if idx != 0:
      return False

    data = self.data.POST.get('data')

    button_id = self.data.POST.get('button_id')

    if not data:
      raise exception.BadRequest(message="Missing data")

    parsed = json.loads(data)

    if button_id == 'accept':
      return self.postAccept(parsed, True)

    if button_id == 'reject':
      return self.postAccept(parsed, False)

    if button_id == 'save':
      return self.postSave(parsed)

  def postAccept(self, data, accept):
    for properties in data:
      if 'full_transfer_key' not in properties:
        logging.warning("Missing key in '%s'", properties)
        continue

      slot_transfer_key = properties['full_transfer_key']
      def accept_slot_transfer_txn():
        slot_transfer = db.get(slot_transfer_key)

        if not slot_transfer:
          logging.warning("Invalid slot_transfer_key '%s'", slot_transfer_key)
          return

        org_key = slot_transfer.parent_key()
        org = ndb.Key.from_old_key(org_key).get()

        if not org:
          logging.warning("No organization present for the slot transfer %s",
                          slot_transfer_key)
          return

        if accept:
          if slot_transfer.status == 'accepted':
            return

          slot_transfer.status = 'accepted'

          if slot_transfer.nr_slots < 0:
            logging.warning(
                "Organization %s is trying to trick us to gain more slots by "
                "using a negative number %s", org.name, slot_transfer.nr_slots)
            return

          org.slot_allocation -= slot_transfer.nr_slots
          if org.slot_allocation < 0:
            org.slot_allocation = 0

          org.put()
        else:
          if slot_transfer.status == 'rejected':
            return

          if slot_transfer.status == 'accepted':
            org.slot_allocation += slot_transfer.nr_slots
            org.put()

          slot_transfer.status = 'rejected'
        slot_transfer.put()

      # TODO(daniel): run this function in transaction when GSoCSlotTransfer
      # is updated to NDB
      accept_slot_transfer_txn()

    return True

  def postSave(self, parsed):

    for key_name, properties in parsed.iteritems():
      admin_remarks = properties.get('admin_remarks')
      nr_slots = properties.get('nr_slots')
      full_transfer_key = properties.get('full_transfer_key')

      if not full_transfer_key:
        logging.warning(
            "key for the slot transfer request is not present '%s'",
            properties)
        continue

      if 'admin_remarks' not in properties and 'nr_slots' not in properties:
        logging.warning(
            "Neither admin remarks or number of slots present in '%s'",
            properties)
        continue

      if 'nr_slots' in properties:
        if not nr_slots.isdigit():
{'fraction_non_alphanumeric': 0.10201288244766506, 'fraction_numerical': 0.0045088566827697265, 'mean_word_length': 2.97472, 'pattern_counts': {'":': 0, '<': 7, '<?xml version=': 0, '>': 3, 'https://': 0, 'lorem ipsum': 0, 'www.': 2, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 2, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'dir': 'git-commits-cleaned', 'id': '2006180', 'n_tokens_mistral': 3619, 'n_tokens_neox': 3527, 'n_words': 980}